LocalDisplayAdapter.java revision 44b1f76474218349c9327da9fe482e19aad98795
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.util.SparseArray; 25import android.view.Display; 26import android.view.DisplayEventReceiver; 27import android.view.Surface; 28import android.view.SurfaceControl; 29import android.view.SurfaceControl.PhysicalDisplayInfo; 30 31import java.io.PrintWriter; 32 33/** 34 * A display adapter for the local displays managed by Surface Flinger. 35 * <p> 36 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 37 * </p> 38 */ 39final class LocalDisplayAdapter extends DisplayAdapter { 40 private static final String TAG = "LocalDisplayAdapter"; 41 42 private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] { 43 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN, 44 SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI, 45 }; 46 47 private final SparseArray<LocalDisplayDevice> mDevices = 48 new SparseArray<LocalDisplayDevice>(); 49 private HotplugDisplayEventReceiver mHotplugReceiver; 50 51 private final SurfaceControl.PhysicalDisplayInfo mTempPhys = new SurfaceControl.PhysicalDisplayInfo(); 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 && SurfaceControl.getDisplayInfo(displayToken, mTempPhys)) { 73 LocalDisplayDevice device = mDevices.get(builtInDisplayId); 74 if (device == null) { 75 // Display was added. 76 device = new LocalDisplayDevice(displayToken, builtInDisplayId, mTempPhys); 77 mDevices.put(builtInDisplayId, device); 78 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); 79 } else if (device.updatePhysicalDisplayInfoLocked(mTempPhys)) { 80 // Display properties changed. 81 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); 82 } 83 } else { 84 // The display is no longer available. Ignore the attempt to add it. 85 // If it was connected but has already been disconnected, we'll get a 86 // disconnect event that will remove it from mDevices. 87 } 88 } 89 90 private void tryDisconnectDisplayLocked(int builtInDisplayId) { 91 LocalDisplayDevice device = mDevices.get(builtInDisplayId); 92 if (device != null) { 93 // Display was removed. 94 mDevices.remove(builtInDisplayId); 95 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED); 96 } 97 } 98 99 static boolean shouldBlank(int state) { 100 return state == Display.STATE_OFF; 101 } 102 103 static boolean shouldUnblank(int state) { 104 return state == Display.STATE_ON || state == Display.STATE_DOZING; 105 } 106 107 private final class LocalDisplayDevice extends DisplayDevice { 108 private final int mBuiltInDisplayId; 109 private final SurfaceControl.PhysicalDisplayInfo mPhys; 110 111 private DisplayDeviceInfo mInfo; 112 private boolean mHavePendingChanges; 113 private int mState = Display.STATE_UNKNOWN; 114 115 public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId, 116 SurfaceControl.PhysicalDisplayInfo phys) { 117 super(LocalDisplayAdapter.this, displayToken); 118 mBuiltInDisplayId = builtInDisplayId; 119 mPhys = new SurfaceControl.PhysicalDisplayInfo(phys); 120 } 121 122 public boolean updatePhysicalDisplayInfoLocked(SurfaceControl.PhysicalDisplayInfo phys) { 123 if (!mPhys.equals(phys)) { 124 mPhys.copyFrom(phys); 125 mHavePendingChanges = true; 126 return true; 127 } 128 return false; 129 } 130 131 @Override 132 public void applyPendingDisplayDeviceInfoChangesLocked() { 133 if (mHavePendingChanges) { 134 mInfo = null; 135 mHavePendingChanges = false; 136 } 137 } 138 139 @Override 140 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 141 if (mInfo == null) { 142 mInfo = new DisplayDeviceInfo(); 143 mInfo.width = mPhys.width; 144 mInfo.height = mPhys.height; 145 mInfo.refreshRate = mPhys.refreshRate; 146 mInfo.state = mState; 147 148 // Assume that all built-in displays that have secure output (eg. HDCP) also 149 // support compositing from gralloc protected buffers. 150 if (mPhys.secure) { 151 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE 152 | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; 153 } 154 155 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { 156 mInfo.name = getContext().getResources().getString( 157 com.android.internal.R.string.display_manager_built_in_display_name); 158 mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY 159 | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; 160 mInfo.type = Display.TYPE_BUILT_IN; 161 mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f); 162 mInfo.xDpi = mPhys.xDpi; 163 mInfo.yDpi = mPhys.yDpi; 164 mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL; 165 } else { 166 mInfo.type = Display.TYPE_HDMI; 167 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; 168 mInfo.name = getContext().getResources().getString( 169 com.android.internal.R.string.display_manager_hdmi_display_name); 170 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 171 mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height); 172 173 // For demonstration purposes, allow rotation of the external display. 174 // In the future we might allow the user to configure this directly. 175 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { 176 mInfo.rotation = Surface.ROTATION_270; 177 } 178 } 179 } 180 return mInfo; 181 } 182 183 @Override 184 public void requestDisplayStateLocked(int state) { 185 if (mState != state) { 186 if (shouldBlank(state) && !shouldBlank(mState)) { 187 SurfaceControl.blankDisplay(getDisplayTokenLocked()); 188 } else if (shouldUnblank(state) && !shouldUnblank(mState)) { 189 SurfaceControl.unblankDisplay(getDisplayTokenLocked()); 190 } 191 mState = state; 192 updateDeviceInfoLocked(); 193 } 194 } 195 196 @Override 197 public void dumpLocked(PrintWriter pw) { 198 super.dumpLocked(pw); 199 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); 200 pw.println("mPhys=" + mPhys); 201 pw.println("mState=" + Display.stateToString(mState)); 202 } 203 204 private void updateDeviceInfoLocked() { 205 mInfo = null; 206 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 207 } 208 } 209 210 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver { 211 public HotplugDisplayEventReceiver(Looper looper) { 212 super(looper); 213 } 214 215 @Override 216 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { 217 synchronized (getSyncRoot()) { 218 if (connected) { 219 tryConnectDisplayLocked(builtInDisplayId); 220 } else { 221 tryDisconnectDisplayLocked(builtInDisplayId); 222 } 223 } 224 } 225 } 226}