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 private final class LocalDisplayDevice extends DisplayDevice { 100 private final int mBuiltInDisplayId; 101 private final SurfaceControl.PhysicalDisplayInfo mPhys; 102 103 private DisplayDeviceInfo mInfo; 104 private boolean mHavePendingChanges; 105 private boolean mBlanked; 106 107 public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId, 108 SurfaceControl.PhysicalDisplayInfo phys) { 109 super(LocalDisplayAdapter.this, displayToken); 110 mBuiltInDisplayId = builtInDisplayId; 111 mPhys = new SurfaceControl.PhysicalDisplayInfo(phys); 112 } 113 114 public boolean updatePhysicalDisplayInfoLocked(SurfaceControl.PhysicalDisplayInfo phys) { 115 if (!mPhys.equals(phys)) { 116 mPhys.copyFrom(phys); 117 mHavePendingChanges = true; 118 return true; 119 } 120 return false; 121 } 122 123 @Override 124 public void applyPendingDisplayDeviceInfoChangesLocked() { 125 if (mHavePendingChanges) { 126 mInfo = null; 127 mHavePendingChanges = false; 128 } 129 } 130 131 @Override 132 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 133 if (mInfo == null) { 134 mInfo = new DisplayDeviceInfo(); 135 mInfo.width = mPhys.width; 136 mInfo.height = mPhys.height; 137 mInfo.refreshRate = mPhys.refreshRate; 138 139 // Assume that all built-in displays that have secure output (eg. HDCP) also 140 // support compositing from gralloc protected buffers. 141 if (mPhys.secure) { 142 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE 143 | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; 144 } 145 146 if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { 147 mInfo.name = getContext().getResources().getString( 148 com.android.internal.R.string.display_manager_built_in_display_name); 149 mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY 150 | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; 151 mInfo.type = Display.TYPE_BUILT_IN; 152 mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f); 153 mInfo.xDpi = mPhys.xDpi; 154 mInfo.yDpi = mPhys.yDpi; 155 mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL; 156 } else { 157 mInfo.type = Display.TYPE_HDMI; 158 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; 159 mInfo.name = getContext().getResources().getString( 160 com.android.internal.R.string.display_manager_hdmi_display_name); 161 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 162 mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height); 163 164 // For demonstration purposes, allow rotation of the external display. 165 // In the future we might allow the user to configure this directly. 166 if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { 167 mInfo.rotation = Surface.ROTATION_270; 168 } 169 } 170 } 171 return mInfo; 172 } 173 174 @Override 175 public void blankLocked() { 176 mBlanked = true; 177 SurfaceControl.blankDisplay(getDisplayTokenLocked()); 178 } 179 180 @Override 181 public void unblankLocked() { 182 mBlanked = false; 183 SurfaceControl.unblankDisplay(getDisplayTokenLocked()); 184 } 185 186 @Override 187 public void dumpLocked(PrintWriter pw) { 188 super.dumpLocked(pw); 189 pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); 190 pw.println("mPhys=" + mPhys); 191 pw.println("mBlanked=" + mBlanked); 192 } 193 } 194 195 private final class HotplugDisplayEventReceiver extends DisplayEventReceiver { 196 public HotplugDisplayEventReceiver(Looper looper) { 197 super(looper); 198 } 199 200 @Override 201 public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) { 202 synchronized (getSyncRoot()) { 203 if (connected) { 204 tryConnectDisplayLocked(builtInDisplayId); 205 } else { 206 tryDisconnectDisplayLocked(builtInDisplayId); 207 } 208 } 209 } 210 } 211}