LocalDisplayAdapter.java revision 361ca21acc0831a9f8bbb259bb30218c252a2aa0
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 17package com.android.server.display; 18 19import android.content.Context; 20import android.os.Handler; 21import android.os.IBinder; 22import android.os.Looper; 23import android.os.SystemProperties; 24import android.os.Trace; 25import android.util.Slog; 26import android.util.SparseArray; 27import android.view.Display; 28import android.view.DisplayEventReceiver; 29import android.view.Surface; 30import android.view.SurfaceControl; 31 32import java.io.PrintWriter; 33import 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 */ 41final class LocalDisplayAdapter extends DisplayAdapter { 42 private static final String TAG = "LocalDisplayAdapter"; 43 44 private static final String UNIQUE_ID_PREFIX = "local:"; 45 46 private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] { 47 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN, 48 SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI, 49 }; 50 51 private final SparseArray<LocalDisplayDevice> mDevices = 52 new SparseArray<LocalDisplayDevice>(); 53 private HotplugDisplayEventReceiver mHotplugReceiver; 54 55 // Called with SyncRoot lock held. 56 public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 57 Context context, Handler handler, Listener listener) { 58 super(syncRoot, context, handler, listener, TAG); 59 } 60 61 @Override 62 public void registerLocked() { 63 super.registerLocked(); 64 65 mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper()); 66 67 for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) { 68 tryConnectDisplayLocked(builtInDisplayId); 69 } 70 } 71 72 private void tryConnectDisplayLocked(int builtInDisplayId) { 73 IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId); 74 if (displayToken != null) { 75 SurfaceControl.PhysicalDisplayInfo[] configs = 76 SurfaceControl.getDisplayConfigs(displayToken); 77 if (configs == null) { 78 // There are no valid configs for this device, so we can't use it 79 Slog.w(TAG, "No valid configs found for display device " + 80 builtInDisplayId); 81 return; 82 } 83 int activeConfig = SurfaceControl.getActiveConfig(displayToken); 84 if (activeConfig < 0) { 85 // There is no active config, and for now we don't have the 86 // policy to set one. 87 Slog.w(TAG, "No active config found for display device " + 88 builtInDisplayId); 89 return; 90 } 91 LocalDisplayDevice device = mDevices.get(builtInDisplayId); 92 if (device == null) { 93 // Display was added. 94 device = new LocalDisplayDevice(displayToken, builtInDisplayId, 95 configs, activeConfig); 96 mDevices.put(builtInDisplayId, device); 97 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); 98 } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) { 99 // Display properties changed. 100 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); 101 } 102 } else { 103 // The display is no longer available. Ignore the attempt to add it. 104 // If it was connected but has already been disconnected, we'll get a 105 // disconnect event that will remove it from mDevices. 106 } 107 } 108 109 private void tryDisconnectDisplayLocked(int builtInDisplayId) { 110 LocalDisplayDevice device = mDevices.get(builtInDisplayId); 111 if (device != null) { 112 // Display was removed. 113 mDevices.remove(builtInDisplayId); 114 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED); 115 } 116 } 117 118 static int getPowerModeForState(int state) { 119 switch (state) { 120 case Display.STATE_OFF: 121 return SurfaceControl.POWER_MODE_OFF; 122 case Display.STATE_DOZE: 123 return SurfaceControl.POWER_MODE_DOZE; 124 case Display.STATE_DOZE_SUSPEND: 125 return SurfaceControl.POWER_MODE_DOZE_SUSPEND; 126 default: 127 return SurfaceControl.POWER_MODE_NORMAL; 128 } 129 } 130 131 private final class LocalDisplayDevice extends DisplayDevice { 132 private final int mBuiltInDisplayId; 133 private final SurfaceControl.PhysicalDisplayInfo mPhys; 134 private final int mDefaultPhysicalDisplayInfo; 135 136 private DisplayDeviceInfo mInfo; 137 private boolean mHavePendingChanges; 138 private int mState = Display.STATE_UNKNOWN; 139 private float[] mSupportedRefreshRates; 140 private int[] mRefreshRateConfigIndices; 141 private float mLastRequestedRefreshRate; 142 143 public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId, 144 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) { 145 super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + builtInDisplayId); 146 mBuiltInDisplayId = builtInDisplayId; 147 mPhys = new SurfaceControl.PhysicalDisplayInfo( 148 physicalDisplayInfos[activeDisplayInfo]); 149 mDefaultPhysicalDisplayInfo = activeDisplayInfo; 150 updateSupportedRefreshRatesLocked(physicalDisplayInfos, mPhys); 151 } 152 153 public boolean updatePhysicalDisplayInfoLocked( 154 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) { 155 SurfaceControl.PhysicalDisplayInfo newPhys = physicalDisplayInfos[activeDisplayInfo]; 156 if (!mPhys.equals(newPhys)) { 157 mPhys.copyFrom(newPhys); 158 updateSupportedRefreshRatesLocked(physicalDisplayInfos, mPhys); 159 mHavePendingChanges = true; 160 return true; 161 } 162 return false; 163 } 164 165 @Override 166 public void applyPendingDisplayDeviceInfoChangesLocked() { 167 if (mHavePendingChanges) { 168 mInfo = null; 169 mHavePendingChanges = false; 170 } 171 } 172 173 @Override 174 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 175 if (mInfo == null) { 176 mInfo = new DisplayDeviceInfo(); 177 mInfo.width = mPhys.width; 178 mInfo.height = mPhys.height; 179 mInfo.refreshRate = mPhys.refreshRate; 180 mInfo.supportedRefreshRates = mSupportedRefreshRates; 181 mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos; 182 mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos; 183 mInfo.state = mState; 184 mInfo.uniqueId = getUniqueId(); 185 186 // Assume that all built-in displays that have secure output (eg. HDCP) also 187 // support compositing from gralloc protected buffers. 188 if (mPhys.secure) { 189 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE 190 | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; 191 } 192 193 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { 194 mInfo.name = getContext().getResources().getString( 195 com.android.internal.R.string.display_manager_built_in_display_name); 196 mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY 197 | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; 198 mInfo.type = Display.TYPE_BUILT_IN; 199 mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f); 200 mInfo.xDpi = mPhys.xDpi; 201 mInfo.yDpi = mPhys.yDpi; 202 mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL; 203 } else { 204 mInfo.type = Display.TYPE_HDMI; 205 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; 206 mInfo.name = getContext().getResources().getString( 207 com.android.internal.R.string.display_manager_hdmi_display_name); 208 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 209 mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height); 210 211 // For demonstration purposes, allow rotation of the external display. 212 // In the future we might allow the user to configure this directly. 213 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { 214 mInfo.rotation = Surface.ROTATION_270; 215 } 216 217 // For demonstration purposes, allow rotation of the external display 218 // to follow the built-in display. 219 if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) { 220 mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; 221 } 222 } 223 } 224 return mInfo; 225 } 226 227 @Override 228 public Runnable requestDisplayStateLocked(final int state) { 229 if (mState != state) { 230 final int displayId = mBuiltInDisplayId; 231 final IBinder token = getDisplayTokenLocked(); 232 final int mode = getPowerModeForState(state); 233 mState = state; 234 updateDeviceInfoLocked(); 235 236 // Defer actually setting the display power mode until we have exited 237 // the critical section since it can take hundreds of milliseconds 238 // to complete. 239 return new Runnable() { 240 @Override 241 public void run() { 242 Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState(" 243 + Display.stateToString(state) + ", id=" + displayId + ")"); 244 try { 245 SurfaceControl.setDisplayPowerMode(token, mode); 246 } finally { 247 Trace.traceEnd(Trace.TRACE_TAG_POWER); 248 } 249 } 250 }; 251 } 252 return null; 253 } 254 255 @Override 256 public void requestRefreshRateLocked(float refreshRate) { 257 if (mLastRequestedRefreshRate == refreshRate) { 258 return; 259 } 260 mLastRequestedRefreshRate = refreshRate; 261 if (refreshRate != 0) { 262 final int N = mSupportedRefreshRates.length; 263 for (int i = 0; i < N; i++) { 264 if (refreshRate == mSupportedRefreshRates[i]) { 265 final int configIndex = mRefreshRateConfigIndices[i]; 266 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), configIndex); 267 return; 268 } 269 } 270 Slog.w(TAG, "Requested refresh rate " + refreshRate + " is unsupported."); 271 } 272 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), mDefaultPhysicalDisplayInfo); 273 } 274 275 @Override 276 public void dumpLocked(PrintWriter pw) { 277 super.dumpLocked(pw); 278 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); 279 pw.println("mPhys=" + mPhys); 280 pw.println("mState=" + Display.stateToString(mState)); 281 } 282 283 private void updateDeviceInfoLocked() { 284 mInfo = null; 285 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 286 } 287 288 private void updateSupportedRefreshRatesLocked( 289 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, 290 SurfaceControl.PhysicalDisplayInfo activePhys) { 291 final int N = physicalDisplayInfos.length; 292 int idx = 0; 293 mSupportedRefreshRates = new float[N]; 294 mRefreshRateConfigIndices = new int[N]; 295 for (int i = 0; i < N; i++) { 296 final SurfaceControl.PhysicalDisplayInfo phys = physicalDisplayInfos[i]; 297 if (activePhys.width == phys.width 298 && activePhys.height == phys.height 299 && activePhys.density == phys.density 300 && activePhys.xDpi == phys.xDpi 301 && activePhys.yDpi == phys.yDpi) { 302 mSupportedRefreshRates[idx] = phys.refreshRate; 303 mRefreshRateConfigIndices[idx++] = i; 304 } 305 } 306 if (idx != N) { 307 mSupportedRefreshRates = Arrays.copyOfRange(mSupportedRefreshRates, 0, idx); 308 mRefreshRateConfigIndices = Arrays.copyOfRange(mRefreshRateConfigIndices, 0, idx); 309 } 310 } 311 } 312 313 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver { 314 public HotplugDisplayEventReceiver(Looper looper) { 315 super(looper); 316 } 317 318 @Override 319 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { 320 synchronized (getSyncRoot()) { 321 if (connected) { 322 tryConnectDisplayLocked(builtInDisplayId); 323 } else { 324 tryDisconnectDisplayLocked(builtInDisplayId); 325 } 326 } 327 } 328 } 329} 330