Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 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 | |
| 17 | package com.android.server.display; |
| 18 | |
| 19 | import android.Manifest; |
| 20 | import android.content.Context; |
| 21 | import android.content.pm.PackageManager; |
| 22 | import android.hardware.display.IDisplayManager; |
| 23 | import android.os.Binder; |
| 24 | import android.os.SystemProperties; |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 25 | import android.util.Slog; |
| 26 | import android.util.SparseArray; |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 27 | import android.view.Display; |
| 28 | import android.view.DisplayInfo; |
| 29 | import android.view.Surface; |
| 30 | |
| 31 | import java.io.FileDescriptor; |
| 32 | import java.io.PrintWriter; |
| 33 | import java.util.ArrayList; |
| 34 | |
| 35 | /** |
| 36 | * Manages the properties, media routing and power state of attached displays. |
| 37 | * <p> |
| 38 | * The display manager service does not own or directly control the displays. |
| 39 | * Instead, other components in the system register their display adapters with the |
| 40 | * display manager service which acts as a central controller. |
| 41 | * </p> |
| 42 | */ |
| 43 | public final class DisplayManagerService extends IDisplayManager.Stub { |
| 44 | private static final String TAG = "DisplayManagerService"; |
| 45 | |
| 46 | private static final String SYSTEM_HEADLESS = "ro.config.headless"; |
| 47 | |
| 48 | private final Object mLock = new Object(); |
| 49 | |
| 50 | private Context mContext; |
| 51 | private final boolean mHeadless; |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 52 | |
| 53 | private int mDisplayIdSeq = Display.DEFAULT_DISPLAY; |
| 54 | |
| 55 | /** All registered DisplayAdapters. */ |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 56 | private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>(); |
| 57 | |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 58 | /** All the DisplayAdapters showing the given displayId. */ |
| 59 | private final SparseArray<ArrayList<DisplayAdapter>> mLogicalToPhysicals = |
| 60 | new SparseArray<ArrayList<DisplayAdapter>>(); |
| 61 | |
| 62 | /** All the DisplayInfos in the system indexed by deviceId */ |
| 63 | private final SparseArray<DisplayInfo> mDisplayInfos = new SparseArray<DisplayInfo>(); |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 64 | |
Craig Mautner | 9de4936 | 2012-08-02 14:30:30 -0700 | [diff] [blame] | 65 | private final ArrayList<DisplayCallback> mCallbacks = |
| 66 | new ArrayList<DisplayManagerService.DisplayCallback>(); |
| 67 | |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 68 | public DisplayManagerService() { |
| 69 | mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1"); |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 70 | registerDefaultDisplayAdapter(); |
| 71 | } |
| 72 | |
| 73 | private void registerDefaultDisplayAdapter() { |
| 74 | if (mHeadless) { |
| 75 | registerDisplayAdapter(new HeadlessDisplayAdapter()); |
| 76 | } else { |
| 77 | registerDisplayAdapter(new SurfaceFlingerDisplayAdapter()); |
| 78 | } |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | public void setContext(Context context) { |
| 82 | mContext = context; |
| 83 | } |
| 84 | |
| 85 | // FIXME: this isn't the right API for the long term |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 86 | public void getDefaultExternalDisplayDeviceInfo(DisplayDeviceInfo info) { |
| 87 | // hardcoded assuming 720p touch screen plugged into HDMI and USB |
| 88 | // need to redesign this |
| 89 | info.width = 1280; |
| 90 | info.height = 720; |
| 91 | } |
| 92 | |
| 93 | public boolean isHeadless() { |
| 94 | return mHeadless; |
| 95 | } |
| 96 | |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 97 | /** |
| 98 | * Save away new DisplayInfo data. |
| 99 | * @param displayId The local DisplayInfo to store the new data in. |
| 100 | * @param info The new data to be stored. |
| 101 | */ |
| 102 | public void setDisplayInfo(int displayId, DisplayInfo info) { |
| 103 | synchronized (mLock) { |
| 104 | DisplayInfo localInfo = mDisplayInfos.get(displayId); |
| 105 | if (localInfo == null) { |
| 106 | localInfo = new DisplayInfo(); |
| 107 | mDisplayInfos.put(displayId, localInfo); |
| 108 | } |
| 109 | localInfo.copyFrom(info); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | /** |
| 114 | * Return requested DisplayInfo. |
| 115 | * @param displayId The data to retrieve. |
| 116 | * @param outInfo The structure to receive the data. |
| 117 | */ |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 118 | @Override // Binder call |
| 119 | public boolean getDisplayInfo(int displayId, DisplayInfo outInfo) { |
| 120 | synchronized (mLock) { |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 121 | DisplayInfo localInfo = mDisplayInfos.get(displayId); |
| 122 | if (localInfo == null) { |
| 123 | return false; |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 124 | } |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 125 | outInfo.copyFrom(localInfo); |
| 126 | return true; |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 127 | } |
| 128 | } |
| 129 | |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 130 | /** |
| 131 | * Inform the service of a new physical display. A new logical displayId is created and the new |
| 132 | * physical display is immediately bound to it. Use removeAdapterFromDisplay to disconnect it. |
| 133 | * |
| 134 | * @param adapter The wrapper for information associated with the physical display. |
| 135 | */ |
| 136 | public void registerDisplayAdapter(DisplayAdapter adapter) { |
Craig Mautner | 9de4936 | 2012-08-02 14:30:30 -0700 | [diff] [blame] | 137 | |
| 138 | int displayId; |
| 139 | DisplayCallback[] callbacks; |
| 140 | |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 141 | synchronized (mLock) { |
Craig Mautner | 9de4936 | 2012-08-02 14:30:30 -0700 | [diff] [blame] | 142 | displayId = mDisplayIdSeq; |
| 143 | do { |
| 144 | // Find the next unused displayId. (Pretend like it might ever wrap around). |
| 145 | mDisplayIdSeq++; |
| 146 | if (mDisplayIdSeq < 0) { |
| 147 | mDisplayIdSeq = Display.DEFAULT_DISPLAY + 1; |
| 148 | } |
| 149 | } while (mDisplayInfos.get(mDisplayIdSeq) != null); |
| 150 | |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 151 | adapter.setDisplayId(displayId); |
| 152 | |
| 153 | createDisplayInfoLocked(displayId, adapter); |
| 154 | |
| 155 | ArrayList<DisplayAdapter> list = new ArrayList<DisplayAdapter>(); |
| 156 | list.add(adapter); |
| 157 | mLogicalToPhysicals.put(displayId, list); |
| 158 | |
| 159 | mDisplayAdapters.add(adapter); |
Craig Mautner | 9de4936 | 2012-08-02 14:30:30 -0700 | [diff] [blame] | 160 | callbacks = mCallbacks.toArray(new DisplayCallback[mCallbacks.size()]); |
| 161 | } |
| 162 | |
| 163 | for (int i = callbacks.length - 1; i >= 0; i--) { |
| 164 | callbacks[i].displayAdded(displayId); |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | // TODO: Notify SurfaceFlinger of new addition. |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * Connect a logical display to a physical display. Will remove the physical display from any |
| 172 | * logical display it is currently attached to. |
| 173 | * |
| 174 | * @param displayId The logical display. Will be created if it does not already exist. |
| 175 | * @param adapter The physical display. |
| 176 | */ |
| 177 | public void addAdapterToDisplay(int displayId, DisplayAdapter adapter) { |
| 178 | if (adapter == null) { |
| 179 | // TODO: Or throw NPE? |
| 180 | Slog.e(TAG, "addDeviceToDisplay: Attempt to add null adapter"); |
| 181 | return; |
| 182 | } |
| 183 | |
| 184 | synchronized (mLock) { |
| 185 | if (!mDisplayAdapters.contains(adapter)) { |
| 186 | // TOOD: Handle unregistered adapter with exception or return value. |
| 187 | Slog.e(TAG, "addDeviceToDisplay: Attempt to add an unregistered adapter"); |
| 188 | return; |
| 189 | } |
| 190 | |
| 191 | DisplayInfo displayInfo = mDisplayInfos.get(displayId); |
| 192 | if (displayInfo == null) { |
| 193 | createDisplayInfoLocked(displayId, adapter); |
| 194 | } |
| 195 | |
| 196 | Integer oldDisplayId = adapter.getDisplayId(); |
| 197 | if (oldDisplayId != Display.NO_DISPLAY) { |
| 198 | if (oldDisplayId == displayId) { |
| 199 | // adapter already added to displayId. |
| 200 | return; |
| 201 | } |
| 202 | |
| 203 | removeAdapterLocked(adapter); |
| 204 | } |
| 205 | |
| 206 | ArrayList<DisplayAdapter> list = mLogicalToPhysicals.get(displayId); |
| 207 | if (list == null) { |
| 208 | list = new ArrayList<DisplayAdapter>(); |
| 209 | mLogicalToPhysicals.put(displayId, list); |
| 210 | } |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 211 | list.add(adapter); |
| 212 | adapter.setDisplayId(displayId); |
| 213 | } |
| 214 | |
| 215 | // TODO: Notify SurfaceFlinger of new addition. |
| 216 | } |
| 217 | |
| 218 | /** |
| 219 | * Disconnect the physical display from whichever logical display it is attached to. |
| 220 | * @param adapter The physical display to detach. |
| 221 | */ |
| 222 | public void removeAdapterFromDisplay(DisplayAdapter adapter) { |
| 223 | if (adapter == null) { |
| 224 | // TODO: Or throw NPE? |
| 225 | return; |
| 226 | } |
| 227 | |
| 228 | synchronized (mLock) { |
| 229 | if (!mDisplayAdapters.contains(adapter)) { |
| 230 | // TOOD: Handle unregistered adapter with exception or return value. |
| 231 | Slog.e(TAG, "removeDeviceFromDisplay: Attempt to remove an unregistered adapter"); |
| 232 | return; |
| 233 | } |
| 234 | |
| 235 | removeAdapterLocked(adapter); |
| 236 | } |
| 237 | |
| 238 | // TODO: Notify SurfaceFlinger of removal. |
| 239 | } |
| 240 | |
Craig Mautner | 9de4936 | 2012-08-02 14:30:30 -0700 | [diff] [blame] | 241 | public void registerDisplayCallback(final DisplayCallback callback) { |
| 242 | synchronized (mLock) { |
| 243 | if (!mCallbacks.contains(callback)) { |
| 244 | mCallbacks.add(callback); |
| 245 | } |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | public void unregisterDisplayCallback(final DisplayCallback callback) { |
| 250 | synchronized (mLock) { |
| 251 | mCallbacks.remove(callback); |
| 252 | } |
| 253 | } |
| 254 | |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 255 | /** |
| 256 | * Create a new logical DisplayInfo and fill it in with information from the physical display. |
| 257 | * @param displayId The logical identifier. |
| 258 | * @param adapter The physical display for initial values. |
| 259 | */ |
| 260 | private void createDisplayInfoLocked(int displayId, DisplayAdapter adapter) { |
| 261 | DisplayInfo displayInfo = new DisplayInfo(); |
| 262 | DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); |
| 263 | adapter.getDisplayDevice().getInfo(deviceInfo); |
| 264 | copyDisplayInfoFromDeviceInfo(displayInfo, deviceInfo); |
| 265 | mDisplayInfos.put(displayId, displayInfo); |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * Disconnect a physical display from its logical display. If there are no more physical |
| 270 | * displays attached to the logical display, delete the logical display. |
| 271 | * @param adapter The physical display to detach. |
| 272 | */ |
| 273 | void removeAdapterLocked(DisplayAdapter adapter) { |
| 274 | int displayId = adapter.getDisplayId(); |
| 275 | adapter.setDisplayId(Display.NO_DISPLAY); |
| 276 | |
| 277 | ArrayList<DisplayAdapter> list = mLogicalToPhysicals.get(displayId); |
| 278 | if (list != null) { |
| 279 | list.remove(adapter); |
| 280 | if (list.isEmpty()) { |
| 281 | mLogicalToPhysicals.remove(displayId); |
| 282 | // TODO: Keep count of Windows attached to logical display and don't delete if |
| 283 | // there are any outstanding. Also, what keeps the WindowManager from continuing |
| 284 | // to use the logical display? |
| 285 | mDisplayInfos.remove(displayId); |
| 286 | } |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | private void copyDisplayInfoFromDeviceInfo(DisplayInfo displayInfo, |
| 291 | DisplayDeviceInfo deviceInfo) { |
| 292 | // Bootstrap the logical display using the physical display. |
| 293 | displayInfo.appWidth = deviceInfo.width; |
| 294 | displayInfo.appHeight = deviceInfo.height; |
| 295 | displayInfo.logicalWidth = deviceInfo.width; |
| 296 | displayInfo.logicalHeight = deviceInfo.height; |
| 297 | displayInfo.rotation = Surface.ROTATION_0; |
| 298 | displayInfo.refreshRate = deviceInfo.refreshRate; |
| 299 | displayInfo.logicalDensityDpi = deviceInfo.densityDpi; |
| 300 | displayInfo.physicalXDpi = deviceInfo.xDpi; |
| 301 | displayInfo.physicalYDpi = deviceInfo.yDpi; |
| 302 | displayInfo.smallestNominalAppWidth = deviceInfo.width; |
| 303 | displayInfo.smallestNominalAppHeight = deviceInfo.height; |
| 304 | displayInfo.largestNominalAppWidth = deviceInfo.width; |
| 305 | displayInfo.largestNominalAppHeight = deviceInfo.height; |
| 306 | } |
| 307 | |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 308 | @Override // Binder call |
| 309 | public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| 310 | if (mContext == null |
| 311 | || mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) |
| 312 | != PackageManager.PERMISSION_GRANTED) { |
| 313 | pw.println("Permission Denial: can't dump DisplayManager from from pid=" |
| 314 | + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); |
| 315 | return; |
| 316 | } |
| 317 | |
| 318 | pw.println("DISPLAY MANAGER (dumpsys display)\n"); |
| 319 | |
| 320 | pw.println("Headless: " + mHeadless); |
| 321 | |
| 322 | DisplayDeviceInfo info = new DisplayDeviceInfo(); |
| 323 | for (DisplayAdapter adapter : mDisplayAdapters) { |
Craig Mautner | 9de4936 | 2012-08-02 14:30:30 -0700 | [diff] [blame] | 324 | pw.println("Display for adapter " + adapter.getName() |
| 325 | + " assigned to Display " + adapter.getDisplayId()); |
Craig Mautner | 4f67ba6 | 2012-08-02 11:23:00 -0700 | [diff] [blame] | 326 | DisplayDevice device = adapter.getDisplayDevice(); |
| 327 | pw.print(" "); |
| 328 | device.getInfo(info); |
| 329 | pw.println(info); |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 330 | } |
| 331 | } |
Craig Mautner | 9de4936 | 2012-08-02 14:30:30 -0700 | [diff] [blame] | 332 | |
| 333 | public interface DisplayCallback { |
| 334 | public void displayAdded(int displayId); |
| 335 | public void displayRemoved(int displayId); |
| 336 | } |
Jeff Brown | fa25bf5 | 2012-07-23 19:26:30 -0700 | [diff] [blame] | 337 | } |