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.Context;
20 import android.os.Handler;
21 import android.os.IBinder;
22 import android.os.Looper;
23 import android.os.SystemProperties;
24 import android.util.Pair;
25 import android.util.Slog;
26 import android.util.SparseArray;
27 import android.view.Display;
28 import android.view.DisplayEventReceiver;
29 import android.view.Surface;
30 import android.view.SurfaceControl;
32 import java.io.PrintWriter;
33 import java.util.Arrays;
36 * A display adapter for the local displays managed by Surface Flinger.
38 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
41 final class LocalDisplayAdapter extends DisplayAdapter {
42 private static final String TAG = "LocalDisplayAdapter";
44 private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
45 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
46 SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
49 private final SparseArray<LocalDisplayDevice> mDevices =
50 new SparseArray<LocalDisplayDevice>();
51 private HotplugDisplayEventReceiver mHotplugReceiver;
53 // Called with SyncRoot lock held.
54 public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
55 Context context, Handler handler, Listener listener) {
56 super(syncRoot, context, handler, listener, TAG);
60 public void registerLocked() {
61 super.registerLocked();
63 mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
65 for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
66 tryConnectDisplayLocked(builtInDisplayId);
70 private void tryConnectDisplayLocked(int builtInDisplayId) {
71 IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
72 if (displayToken != null) {
73 SurfaceControl.PhysicalDisplayInfo[] configs =
74 SurfaceControl.getDisplayConfigs(displayToken);
75 if (configs == null) {
76 // There are no valid configs for this device, so we can't use it
77 Slog.w(TAG, "No valid configs found for display device " +
81 int activeConfig = SurfaceControl.getActiveConfig(displayToken);
82 if (activeConfig < 0) {
83 // There is no active config, and for now we don't have the
85 Slog.w(TAG, "No active config found for display device " +
89 LocalDisplayDevice device = mDevices.get(builtInDisplayId);
92 device = new LocalDisplayDevice(displayToken, builtInDisplayId,
93 configs, activeConfig);
94 mDevices.put(builtInDisplayId, device);
95 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
96 } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) {
97 // Display properties changed.
98 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
101 // The display is no longer available. Ignore the attempt to add it.
102 // If it was connected but has already been disconnected, we'll get a
103 // disconnect event that will remove it from mDevices.
107 private void tryDisconnectDisplayLocked(int builtInDisplayId) {
108 LocalDisplayDevice device = mDevices.get(builtInDisplayId);
109 if (device != null) {
110 // Display was removed.
111 mDevices.remove(builtInDisplayId);
112 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
116 static int getPowerModeForState(int state) {
118 case Display.STATE_OFF:
119 return SurfaceControl.POWER_MODE_OFF;
120 case Display.STATE_DOZE:
121 return SurfaceControl.POWER_MODE_DOZE;
122 case Display.STATE_DOZE_SUSPEND:
123 return SurfaceControl.POWER_MODE_DOZE_SUSPEND;
125 return SurfaceControl.POWER_MODE_NORMAL;
129 private final class LocalDisplayDevice extends DisplayDevice {
130 private final int mBuiltInDisplayId;
131 private final SurfaceControl.PhysicalDisplayInfo mPhys;
132 private final int mDefaultPhysicalDisplayInfo;
134 private DisplayDeviceInfo mInfo;
135 private boolean mHavePendingChanges;
136 private int mState = Display.STATE_UNKNOWN;
137 private float[] mSupportedRefreshRates;
138 private int[] mRefreshRateConfigIndices;
139 private float mLastRequestedRefreshRate;
141 public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
142 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
143 super(LocalDisplayAdapter.this, displayToken);
144 mBuiltInDisplayId = builtInDisplayId;
145 mPhys = new SurfaceControl.PhysicalDisplayInfo(
146 physicalDisplayInfos[activeDisplayInfo]);
147 mDefaultPhysicalDisplayInfo = activeDisplayInfo;
148 updateSupportedRefreshRatesLocked(physicalDisplayInfos, mPhys);
151 public boolean updatePhysicalDisplayInfoLocked(
152 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) {
153 SurfaceControl.PhysicalDisplayInfo newPhys = physicalDisplayInfos[activeDisplayInfo];
154 if (!mPhys.equals(newPhys)) {
155 mPhys.copyFrom(newPhys);
156 updateSupportedRefreshRatesLocked(physicalDisplayInfos, mPhys);
157 mHavePendingChanges = true;
164 public void applyPendingDisplayDeviceInfoChangesLocked() {
165 if (mHavePendingChanges) {
167 mHavePendingChanges = false;
172 public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
174 mInfo = new DisplayDeviceInfo();
175 mInfo.width = mPhys.width;
176 mInfo.height = mPhys.height;
177 mInfo.refreshRate = mPhys.refreshRate;
178 mInfo.supportedRefreshRates = mSupportedRefreshRates;
179 mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos;
180 mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos;
181 mInfo.state = mState;
183 // Assume that all built-in displays that have secure output (eg. HDCP) also
184 // support compositing from gralloc protected buffers.
186 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
187 | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
190 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
191 mInfo.name = getContext().getResources().getString(
192 com.android.internal.R.string.display_manager_built_in_display_name);
193 mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
194 | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
195 mInfo.type = Display.TYPE_BUILT_IN;
196 mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
197 mInfo.xDpi = mPhys.xDpi;
198 mInfo.yDpi = mPhys.yDpi;
199 mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
201 mInfo.type = Display.TYPE_HDMI;
202 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
203 mInfo.name = getContext().getResources().getString(
204 com.android.internal.R.string.display_manager_hdmi_display_name);
205 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
206 mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
208 // For demonstration purposes, allow rotation of the external display.
209 // In the future we might allow the user to configure this directly.
210 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
211 mInfo.rotation = Surface.ROTATION_270;
214 // For demonstration purposes, allow rotation of the external display
215 // to follow the built-in display.
216 if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
217 mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
225 public void requestDisplayStateLocked(int state) {
226 if (mState != state) {
227 SurfaceControl.setDisplayPowerMode(getDisplayTokenLocked(),
228 getPowerModeForState(state));
230 updateDeviceInfoLocked();
235 public void requestRefreshRateLocked(float refreshRate) {
236 if (mLastRequestedRefreshRate == refreshRate) {
239 mLastRequestedRefreshRate = refreshRate;
240 if (refreshRate != 0) {
241 final int N = mSupportedRefreshRates.length;
242 for (int i = 0; i < N; i++) {
243 if (refreshRate == mSupportedRefreshRates[i]) {
244 final int configIndex = mRefreshRateConfigIndices[i];
245 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), configIndex);
249 Slog.w(TAG, "Requested refresh rate " + refreshRate + " is unsupported.");
251 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), mDefaultPhysicalDisplayInfo);
255 public void dumpLocked(PrintWriter pw) {
256 super.dumpLocked(pw);
257 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
258 pw.println("mPhys=" + mPhys);
259 pw.println("mState=" + Display.stateToString(mState));
262 private void updateDeviceInfoLocked() {
264 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
267 private void updateSupportedRefreshRatesLocked(
268 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos,
269 SurfaceControl.PhysicalDisplayInfo activePhys) {
270 final int N = physicalDisplayInfos.length;
272 mSupportedRefreshRates = new float[N];
273 mRefreshRateConfigIndices = new int[N];
274 for (int i = 0; i < N; i++) {
275 final SurfaceControl.PhysicalDisplayInfo phys = physicalDisplayInfos[i];
276 if (activePhys.width == phys.width
277 && activePhys.height == phys.height
278 && activePhys.density == phys.density
279 && activePhys.xDpi == phys.xDpi
280 && activePhys.yDpi == phys.yDpi) {
281 mSupportedRefreshRates[idx] = phys.refreshRate;
282 mRefreshRateConfigIndices[idx++] = i;
286 mSupportedRefreshRates = Arrays.copyOfRange(mSupportedRefreshRates, 0, idx);
287 mRefreshRateConfigIndices = Arrays.copyOfRange(mRefreshRateConfigIndices, 0, idx);
292 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
293 public HotplugDisplayEventReceiver(Looper looper) {
298 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
299 synchronized (getSyncRoot()) {
301 tryConnectDisplayLocked(builtInDisplayId);
303 tryDisconnectDisplayLocked(builtInDisplayId);