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