LocalDisplayAdapter.java revision 3edf5272fb2185403dfe64b9722b9fc9b9de80f8
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 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 void requestDisplayStateLocked(int state) { 226 if (mState != state) { 227 final int mode = getPowerModeForState(state); 228 Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState(" 229 + Display.stateToString(state) + ", id=" + mBuiltInDisplayId + ")"); 230 try { 231 SurfaceControl.setDisplayPowerMode(getDisplayTokenLocked(), mode); 232 } finally { 233 Trace.traceEnd(Trace.TRACE_TAG_POWER); 234 } 235 mState = state; 236 updateDeviceInfoLocked(); 237 } 238 } 239 240 @Override 241 public void requestRefreshRateLocked(float refreshRate) { 242 if (mLastRequestedRefreshRate == refreshRate) { 243 return; 244 } 245 mLastRequestedRefreshRate = refreshRate; 246 if (refreshRate != 0) { 247 final int N = mSupportedRefreshRates.length; 248 for (int i = 0; i < N; i++) { 249 if (refreshRate == mSupportedRefreshRates[i]) { 250 final int configIndex = mRefreshRateConfigIndices[i]; 251 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), configIndex); 252 return; 253 } 254 } 255 Slog.w(TAG, "Requested refresh rate " + refreshRate + " is unsupported."); 256 } 257 SurfaceControl.setActiveConfig(getDisplayTokenLocked(), mDefaultPhysicalDisplayInfo); 258 } 259 260 @Override 261 public void dumpLocked(PrintWriter pw) { 262 super.dumpLocked(pw); 263 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); 264 pw.println("mPhys=" + mPhys); 265 pw.println("mState=" + Display.stateToString(mState)); 266 } 267 268 private void updateDeviceInfoLocked() { 269 mInfo = null; 270 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 271 } 272 273 private void updateSupportedRefreshRatesLocked( 274 SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, 275 SurfaceControl.PhysicalDisplayInfo activePhys) { 276 final int N = physicalDisplayInfos.length; 277 int idx = 0; 278 mSupportedRefreshRates = new float[N]; 279 mRefreshRateConfigIndices = new int[N]; 280 for (int i = 0; i < N; i++) { 281 final SurfaceControl.PhysicalDisplayInfo phys = physicalDisplayInfos[i]; 282 if (activePhys.width == phys.width 283 && activePhys.height == phys.height 284 && activePhys.density == phys.density 285 && activePhys.xDpi == phys.xDpi 286 && activePhys.yDpi == phys.yDpi) { 287 mSupportedRefreshRates[idx] = phys.refreshRate; 288 mRefreshRateConfigIndices[idx++] = i; 289 } 290 } 291 if (idx != N) { 292 mSupportedRefreshRates = Arrays.copyOfRange(mSupportedRefreshRates, 0, idx); 293 mRefreshRateConfigIndices = Arrays.copyOfRange(mRefreshRateConfigIndices, 0, idx); 294 } 295 } 296 } 297 298 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver { 299 public HotplugDisplayEventReceiver(Looper looper) { 300 super(looper); 301 } 302 303 @Override 304 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { 305 synchronized (getSyncRoot()) { 306 if (connected) { 307 tryConnectDisplayLocked(builtInDisplayId); 308 } else { 309 tryDisconnectDisplayLocked(builtInDisplayId); 310 } 311 } 312 } 313 } 314} 315