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