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.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.os.Trace; 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; 31 32 import java.io.PrintWriter; 33 import java.util.Arrays; 34 35 /** 36 * A display adapter for the local displays managed by Surface Flinger. 37 * <p> 38 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 39 * </p> 40 */ 41 final class LocalDisplayAdapter extends DisplayAdapter { 42 private static final String TAG = "LocalDisplayAdapter"; 43 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, 47 }; 48 49 private final SparseArray<LocalDisplayDevice> mDevices = 50 new SparseArray<LocalDisplayDevice>(); 51 private HotplugDisplayEventReceiver mHotplugReceiver; 52 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); 57 } 58 59 @Override 60 public void registerLocked() { 61 super.registerLocked(); 62 63 mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper()); 64 65 for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) { 66 tryConnectDisplayLocked(builtInDisplayId); 67 } 68 } 69 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 " + 78 builtInDisplayId); 79 return; 80 } 81 int activeConfig = SurfaceControl.getActiveConfig(displayToken); 82 if (activeConfig < 0) { 83 // There is no active config, and for now we don't have the 84 // policy to set one. 85 Slog.w(TAG, "No active config found for display device " + 86 builtInDisplayId); 87 return; 88 } 89 LocalDisplayDevice device = mDevices.get(builtInDisplayId); 90 if (device == null) { 91 // Display was added. 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); 99 } 100 } else { 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. 104 } 105 } 106 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); 113 } 114 } 115 116 static int getPowerModeForState(int state) { 117 switch (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; 124 default: 125 return SurfaceControl.POWER_MODE_NORMAL; 126 } 127 } 128 129 private final class LocalDisplayDevice extends DisplayDevice { 130 private final int mBuiltInDisplayId; 131 private final SurfaceControl.PhysicalDisplayInfo mPhys; 132 private final int mDefaultPhysicalDisplayInfo; 133 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; 140 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); 149 } 150 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; 158 return true; 159 } 160 return false; 161 } 162 163 @Override 164 public void applyPendingDisplayDeviceInfoChangesLocked() { 165 if (mHavePendingChanges) { 166 mInfo = null; 167 mHavePendingChanges = false; 168 } 169 } 170 171 @Override 172 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 173 if (mInfo == null) { 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; 182 183 // Assume that all built-in displays that have secure output (eg. HDCP) also 184 // support compositing from gralloc protected buffers. 185 if (mPhys.secure) { 186 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE 187 | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; 188 } 189 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; 200 } else { 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); 207 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; 212 } 213 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; 218 } 219 } 220 } 221 return mInfo; 222 } 223 224 @Override 225 public Runnable requestDisplayStateLocked(final int state) { 226 if (mState != state) { 227 final int displayId = mBuiltInDisplayId; 228 final IBinder token = getDisplayTokenLocked(); 229 final int mode = getPowerModeForState(state); 230 mState = state; 231 updateDeviceInfoLocked(); 232 233 // Defer actually setting the display power mode until we have exited 234 // the critical section since it can take hundreds of milliseconds 235 // to complete. 236 return new Runnable() { 237 @Override 238 public void run() { 239 Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState(" 240 + Display.stateToString(state) + ", id=" + displayId + ")"); 241 try { 242 SurfaceControl.setDisplayPowerMode(token, mode); 243 } finally { 244 Trace.traceEnd(Trace.TRACE_TAG_POWER); 245 } 246 } 247 }; 248 } 249 return null; 250 } 251 252 @Override 253 public void requestRefreshRateLocked(float refreshRate) { 254 if (mLastRequestedRefreshRate == refreshRate) { 255 return; 256 } 257 mLastRequestedRefreshRate = refreshRate; 258 if (refreshRate != 0) { 259 final int N = mSupportedRefreshRates.length; 260 for (int i = 0; i < N; i++) { 261 if (refreshRate == mSupportedRefreshRates[i]) { 262 final int configIndex = mRefreshRateConfigIndices[i]; 263 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), configIndex); 264 return; 265 } 266 } 267 Slog.w(TAG, "Requested refresh rate " + refreshRate + " is unsupported."); 268 } 269 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), mDefaultPhysicalDisplayInfo); 270 } 271 272 @Override 273 public void dumpLocked(PrintWriter pw) { 274 super.dumpLocked(pw); 275 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); 276 pw.println("mPhys=" + mPhys); 277 pw.println("mState=" + Display.stateToString(mState)); 278 } 279 280 private void updateDeviceInfoLocked() { 281 mInfo = null; 282 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 283 } 284 285 private void updateSupportedRefreshRatesLocked( 286 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, 287 SurfaceControl.PhysicalDisplayInfo activePhys) { 288 final int N = physicalDisplayInfos.length; 289 int idx = 0; 290 mSupportedRefreshRates = new float[N]; 291 mRefreshRateConfigIndices = new int[N]; 292 for (int i = 0; i < N; i++) { 293 final SurfaceControl.PhysicalDisplayInfo phys = physicalDisplayInfos[i]; 294 if (activePhys.width == phys.width 295 && activePhys.height == phys.height 296 && activePhys.density == phys.density 297 && activePhys.xDpi == phys.xDpi 298 && activePhys.yDpi == phys.yDpi) { 299 mSupportedRefreshRates[idx] = phys.refreshRate; 300 mRefreshRateConfigIndices[idx++] = i; 301 } 302 } 303 if (idx != N) { 304 mSupportedRefreshRates = Arrays.copyOfRange(mSupportedRefreshRates, 0, idx); 305 mRefreshRateConfigIndices = Arrays.copyOfRange(mRefreshRateConfigIndices, 0, idx); 306 } 307 } 308 } 309 310 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver { 311 public HotplugDisplayEventReceiver(Looper looper) { 312 super(looper); 313 } 314 315 @Override 316 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { 317 synchronized (getSyncRoot()) { 318 if (connected) { 319 tryConnectDisplayLocked(builtInDisplayId); 320 } else { 321 tryDisconnectDisplayLocked(builtInDisplayId); 322 } 323 } 324 } 325 } 326 } 327