/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.display; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.display.IDisplayManager; import android.os.Binder; import android.os.SystemProperties; import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; /** * Manages the properties, media routing and power state of attached displays. *

* The display manager service does not own or directly control the displays. * Instead, other components in the system register their display adapters with the * display manager service which acts as a central controller. *

*/ public final class DisplayManagerService extends IDisplayManager.Stub { private static final String TAG = "DisplayManagerService"; private static final String SYSTEM_HEADLESS = "ro.config.headless"; private final Object mLock = new Object(); private Context mContext; private final boolean mHeadless; private int mDisplayIdSeq = Display.DEFAULT_DISPLAY; /** All registered DisplayAdapters. */ private final ArrayList mDisplayAdapters = new ArrayList(); /** All the DisplayAdapters showing the given displayId. */ private final SparseArray> mLogicalToPhysicals = new SparseArray>(); /** All the DisplayInfos in the system indexed by deviceId */ private final SparseArray mDisplayInfos = new SparseArray(); public DisplayManagerService() { mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1"); registerDefaultDisplayAdapter(); } private void registerDefaultDisplayAdapter() { if (mHeadless) { registerDisplayAdapter(new HeadlessDisplayAdapter()); } else { registerDisplayAdapter(new SurfaceFlingerDisplayAdapter()); } } public void setContext(Context context) { mContext = context; } // FIXME: this isn't the right API for the long term public void getDefaultExternalDisplayDeviceInfo(DisplayDeviceInfo info) { // hardcoded assuming 720p touch screen plugged into HDMI and USB // need to redesign this info.width = 1280; info.height = 720; } public boolean isHeadless() { return mHeadless; } /** * Save away new DisplayInfo data. * @param displayId The local DisplayInfo to store the new data in. * @param info The new data to be stored. */ public void setDisplayInfo(int displayId, DisplayInfo info) { synchronized (mLock) { DisplayInfo localInfo = mDisplayInfos.get(displayId); if (localInfo == null) { localInfo = new DisplayInfo(); mDisplayInfos.put(displayId, localInfo); } localInfo.copyFrom(info); } } /** * Return requested DisplayInfo. * @param displayId The data to retrieve. * @param outInfo The structure to receive the data. */ @Override // Binder call public boolean getDisplayInfo(int displayId, DisplayInfo outInfo) { synchronized (mLock) { DisplayInfo localInfo = mDisplayInfos.get(displayId); if (localInfo == null) { return false; } outInfo.copyFrom(localInfo); return true; } } /** * Inform the service of a new physical display. A new logical displayId is created and the new * physical display is immediately bound to it. Use removeAdapterFromDisplay to disconnect it. * * @param adapter The wrapper for information associated with the physical display. */ public void registerDisplayAdapter(DisplayAdapter adapter) { synchronized (mLock) { int displayId = mDisplayIdSeq++; adapter.setDisplayId(displayId); createDisplayInfoLocked(displayId, adapter); ArrayList list = new ArrayList(); list.add(adapter); mLogicalToPhysicals.put(displayId, list); mDisplayAdapters.add(adapter); } // TODO: Notify SurfaceFlinger of new addition. } /** * Connect a logical display to a physical display. Will remove the physical display from any * logical display it is currently attached to. * * @param displayId The logical display. Will be created if it does not already exist. * @param adapter The physical display. */ public void addAdapterToDisplay(int displayId, DisplayAdapter adapter) { if (adapter == null) { // TODO: Or throw NPE? Slog.e(TAG, "addDeviceToDisplay: Attempt to add null adapter"); return; } synchronized (mLock) { if (!mDisplayAdapters.contains(adapter)) { // TOOD: Handle unregistered adapter with exception or return value. Slog.e(TAG, "addDeviceToDisplay: Attempt to add an unregistered adapter"); return; } DisplayInfo displayInfo = mDisplayInfos.get(displayId); if (displayInfo == null) { createDisplayInfoLocked(displayId, adapter); } Integer oldDisplayId = adapter.getDisplayId(); if (oldDisplayId != Display.NO_DISPLAY) { if (oldDisplayId == displayId) { // adapter already added to displayId. return; } removeAdapterLocked(adapter); } ArrayList list = mLogicalToPhysicals.get(displayId); if (list == null) { list = new ArrayList(); mLogicalToPhysicals.put(displayId, list); } list.add(adapter); adapter.setDisplayId(displayId); } // TODO: Notify SurfaceFlinger of new addition. } /** * Disconnect the physical display from whichever logical display it is attached to. * @param adapter The physical display to detach. */ public void removeAdapterFromDisplay(DisplayAdapter adapter) { if (adapter == null) { // TODO: Or throw NPE? return; } synchronized (mLock) { if (!mDisplayAdapters.contains(adapter)) { // TOOD: Handle unregistered adapter with exception or return value. Slog.e(TAG, "removeDeviceFromDisplay: Attempt to remove an unregistered adapter"); return; } removeAdapterLocked(adapter); } // TODO: Notify SurfaceFlinger of removal. } /** * Create a new logical DisplayInfo and fill it in with information from the physical display. * @param displayId The logical identifier. * @param adapter The physical display for initial values. */ private void createDisplayInfoLocked(int displayId, DisplayAdapter adapter) { DisplayInfo displayInfo = new DisplayInfo(); DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); adapter.getDisplayDevice().getInfo(deviceInfo); copyDisplayInfoFromDeviceInfo(displayInfo, deviceInfo); mDisplayInfos.put(displayId, displayInfo); } /** * Disconnect a physical display from its logical display. If there are no more physical * displays attached to the logical display, delete the logical display. * @param adapter The physical display to detach. */ void removeAdapterLocked(DisplayAdapter adapter) { int displayId = adapter.getDisplayId(); adapter.setDisplayId(Display.NO_DISPLAY); ArrayList list = mLogicalToPhysicals.get(displayId); if (list != null) { list.remove(adapter); if (list.isEmpty()) { mLogicalToPhysicals.remove(displayId); // TODO: Keep count of Windows attached to logical display and don't delete if // there are any outstanding. Also, what keeps the WindowManager from continuing // to use the logical display? mDisplayInfos.remove(displayId); } } } private void copyDisplayInfoFromDeviceInfo(DisplayInfo displayInfo, DisplayDeviceInfo deviceInfo) { // Bootstrap the logical display using the physical display. displayInfo.appWidth = deviceInfo.width; displayInfo.appHeight = deviceInfo.height; displayInfo.logicalWidth = deviceInfo.width; displayInfo.logicalHeight = deviceInfo.height; displayInfo.rotation = Surface.ROTATION_0; displayInfo.refreshRate = deviceInfo.refreshRate; displayInfo.logicalDensityDpi = deviceInfo.densityDpi; displayInfo.physicalXDpi = deviceInfo.xDpi; displayInfo.physicalYDpi = deviceInfo.yDpi; displayInfo.smallestNominalAppWidth = deviceInfo.width; displayInfo.smallestNominalAppHeight = deviceInfo.height; displayInfo.largestNominalAppWidth = deviceInfo.width; displayInfo.largestNominalAppHeight = deviceInfo.height; } @Override // Binder call public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext == null || mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump DisplayManager from from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); return; } pw.println("DISPLAY MANAGER (dumpsys display)\n"); pw.println("Headless: " + mHeadless); DisplayDeviceInfo info = new DisplayDeviceInfo(); for (DisplayAdapter adapter : mDisplayAdapters) { pw.println("Display for adapter " + adapter.getName()); DisplayDevice device = adapter.getDisplayDevice(); pw.print(" "); device.getInfo(info); pw.println(info); } } }