Files
frameworks_base/services/java/com/android/server/display/DisplayManagerService.java
Jeff Brown bd6e1500ae Add initial multi-display support.
Split the DisplayManager into two parts.  One part is bound
to a Context and takes care of Display compatibility and
caching Display objects on behalf of the Context.  The other
part is global and takes care of communicating with the
DisplayManagerService, handling callbacks, and caching
DisplayInfo objects on behalf of the process.

Implemented support for enumerating Displays and getting
callbacks when displays are added, removed or changed.

Elaborated the roles of DisplayManagerService, DisplayAdapter,
and DisplayDevice.  We now support having multiple display
adapters registered, each of which can register multiple display
devices and configure them dynamically.

Added an OverlayDisplayAdapter which is used to simulate
secondary displays by means of overlay windows.  Different
configurations of overlays can be selected using a new
setting in the Developer Settings panel.  The overlays can
be repositioned and resized by the user for convenience.

At the moment, all displays are mirrors of display 0 and
no display transformations are applied.  This will be improved
in future patches.

Refactored the way that the window manager creates its threads.
The OverlayDisplayAdapter needs to be able to use hardware
acceleration so it must share the same UI thread as the Keyguard
and window manager policy.  We now handle this explicitly as
part of starting up the system server.  This puts us in a
better position to consider how we might want to share (or not
share) Loopers among components.

Overlay displays are disabled when in safe mode or in only-core
mode to reduce the number of dependencies started in these modes.

Change-Id: Ic2a661d5448dde01b095ab150697cb6791d69bb5
2012-08-29 15:34:17 -07:00

599 lines
23 KiB
Java

/*
* 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 com.android.internal.util.IndentingPrintWriter;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.IDisplayManager;
import android.hardware.display.IDisplayManagerCallback;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
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.io.StringWriter;
import java.util.ArrayList;
/**
* Manages attached displays.
* <p>
* The {@link DisplayManagerService} manages the global lifecycle of displays,
* decides how to configure logical displays based on the physical display devices currently
* attached, sends notifications to the system and to applications when the state
* changes, and so on.
* </p><p>
* The display manager service relies on a collection of {@link DisplayAdapter} components,
* for discovering and configuring physical display devices attached to the system.
* There are separate display adapters for each manner that devices are attached:
* one display adapter for built-in local displays, one for simulated non-functional
* displays when the system is headless, one for simulated overlay displays used for
* development, one for wifi displays, etc.
* </p><p>
* Display adapters are only weakly coupled to the display manager service.
* Display adapters communicate changes in display device state to the display manager
* service asynchronously via a {@link DisplayAdapter.DisplayAdapterListener} registered
* by the display manager service. This separation of concerns is important for
* two main reasons. First, it neatly encapsulates the responsibilities of these
* two classes: display adapters handle individual display devices whereas
* the display manager service handles the global state. Second, it eliminates
* the potential for deadlocks resulting from asynchronous display device discovery.
* </p><p>
* To keep things simple, display adapters and display devices are single-threaded
* and are only accessed on the display manager's handler thread. Of course, the
* display manager must be accessible by multiple thread (especially for
* incoming binder calls) so all of the display manager's state is synchronized
* and guarded by a lock.
* </p>
*/
public final class DisplayManagerService extends IDisplayManager.Stub {
private static final String TAG = "DisplayManagerService";
private static final boolean DEBUG = false;
private static final String SYSTEM_HEADLESS = "ro.config.headless";
private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER = 1;
private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
private final Object mLock = new Object();
private final Context mContext;
private final boolean mHeadless;
private final DisplayManagerHandler mHandler;
private final DisplayAdapterListener mDisplayAdapterListener = new DisplayAdapterListener();
private final SparseArray<CallbackRecord> mCallbacks =
new SparseArray<CallbackRecord>();
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
// List of all currently connected display devices.
private final ArrayList<DisplayDevice> mDisplayDevices = new ArrayList<DisplayDevice>();
// List of all logical displays, indexed by logical display id.
private final SparseArray<LogicalDisplay> mLogicalDisplays = new SparseArray<LogicalDisplay>();
private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
// True if in safe mode.
// This option may disable certain display adapters.
private boolean mSafeMode;
// True if we are in a special boot mode where only core applications and
// services should be started. This option may disable certain display adapters.
private boolean mOnlyCore;
// Temporary callback list, used when sending display events to applications.
private ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>();
public DisplayManagerService(Context context, Handler uiHandler) {
mContext = context;
mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
mHandler = new DisplayManagerHandler(uiHandler.getLooper());
mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
}
/**
* Pauses the boot process to wait for the first display to be initialized.
*/
public boolean waitForDefaultDisplay() {
synchronized (mLock) {
long timeout = SystemClock.uptimeMillis() + WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null) {
long delay = timeout - SystemClock.uptimeMillis();
if (delay <= 0) {
return false;
}
if (DEBUG) {
Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay);
}
try {
mLock.wait(delay);
} catch (InterruptedException ex) {
}
}
}
return true;
}
/**
* Called when the system is ready to go.
*/
public void systemReady(boolean safeMode, boolean onlyCore) {
synchronized (mLock) {
mSafeMode = safeMode;
mOnlyCore = onlyCore;
}
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
}
// Runs on handler.
private void registerDefaultDisplayAdapter() {
// Register default display adapter.
if (mHeadless) {
registerDisplayAdapter(new HeadlessDisplayAdapter(mContext));
} else {
registerDisplayAdapter(new LocalDisplayAdapter(mContext));
}
}
// Runs on handler.
private void registerAdditionalDisplayAdapters() {
if (shouldRegisterNonEssentialDisplayAdapters()) {
registerDisplayAdapter(new OverlayDisplayAdapter(mContext));
}
}
private boolean shouldRegisterNonEssentialDisplayAdapters() {
// In safe mode, we disable non-essential display adapters to give the user
// an opportunity to fix broken settings or other problems that might affect
// system stability.
// In only-core mode, we disable non-essential display adapters to minimize
// the number of dependencies that are started while in this mode and to
// prevent problems that might occur due to the device being encrypted.
synchronized (mLock) {
return !mSafeMode && !mOnlyCore;
}
}
// Runs on handler.
private void registerDisplayAdapter(DisplayAdapter adapter) {
synchronized (mLock) {
mDisplayAdapters.add(adapter);
}
adapter.register(mDisplayAdapterListener);
}
// 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;
}
/**
* Returns true if the device is headless.
*
* @return True if the device is headless.
*/
public boolean isHeadless() {
return mHeadless;
}
/**
* Sets the new logical display orientation.
*
* @param displayId The logical display id.
* @param orientation One of the Surface.ROTATION_* constants.
*/
public void setDisplayOrientation(int displayId, int orientation) {
synchronized (mLock) {
// TODO: update mirror transforms
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display != null && display.mPrimaryDisplayDevice != null) {
IBinder displayToken = display.mPrimaryDisplayDevice.getDisplayToken();
if (displayToken != null) {
Surface.openTransaction();
try {
Surface.setDisplayOrientation(displayToken, orientation);
} finally {
Surface.closeTransaction();
}
}
display.mBaseDisplayInfo.rotation = orientation;
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
}
}
}
/**
* Overrides the display information of a particular logical display.
* This is used by the window manager to control the size and characteristics
* of the default display.
*
* @param displayId The logical display id.
* @param info The new data to be stored.
*/
public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) {
synchronized (mLock) {
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display != null) {
if (info != null) {
if (display.mOverrideDisplayInfo == null) {
display.mOverrideDisplayInfo = new DisplayInfo();
}
display.mOverrideDisplayInfo.copyFrom(info);
} else {
display.mOverrideDisplayInfo = null;
}
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
}
}
}
/**
* Returns information about the specified logical display.
*
* @param displayId The logical display id.
* @param The logical display info, or null if the display does not exist.
*/
@Override // Binder call
public DisplayInfo getDisplayInfo(int displayId) {
synchronized (mLock) {
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display != null) {
if (display.mOverrideDisplayInfo != null) {
return new DisplayInfo(display.mOverrideDisplayInfo);
}
return new DisplayInfo(display.mBaseDisplayInfo);
}
return null;
}
}
@Override // Binder call
public int[] getDisplayIds() {
synchronized (mLock) {
final int count = mLogicalDisplays.size();
int[] displayIds = new int[count];
for (int i = 0; i > count; i++) {
displayIds[i] = mLogicalDisplays.keyAt(i);
}
return displayIds;
}
}
@Override // Binder call
public void registerCallback(IDisplayManagerCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("listener must not be null");
}
synchronized (mLock) {
int callingPid = Binder.getCallingPid();
if (mCallbacks.get(callingPid) != null) {
throw new SecurityException("The calling process has already "
+ "registered an IDisplayManagerCallback.");
}
CallbackRecord record = new CallbackRecord(callingPid, callback);
try {
IBinder binder = callback.asBinder();
binder.linkToDeath(record, 0);
} catch (RemoteException ex) {
// give up
throw new RuntimeException(ex);
}
mCallbacks.put(callingPid, record);
}
}
private void onCallbackDied(int pid) {
synchronized (mLock) {
mCallbacks.remove(pid);
}
}
// Runs on handler.
private void handleDisplayDeviceAdded(DisplayDevice device) {
synchronized (mLock) {
if (mDisplayDevices.contains(device)) {
Slog.w(TAG, "Attempted to add already added display device: " + device);
return;
}
mDisplayDevices.add(device);
LogicalDisplay display = new LogicalDisplay(device);
display.updateFromPrimaryDisplayDevice();
boolean isDefault = (display.mPrimaryDisplayDeviceInfo.flags
& DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
Slog.w(TAG, "Attempted to add a second default device: " + device);
isDefault = false;
}
int displayId = isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
mLogicalDisplays.put(displayId, display);
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
// Wake up waitForDefaultDisplay.
if (isDefault) {
mLock.notifyAll();
}
}
}
// Runs on handler.
private void handleDisplayDeviceChanged(DisplayDevice device) {
synchronized (mLock) {
if (!mDisplayDevices.contains(device)) {
Slog.w(TAG, "Attempted to change non-existent display device: " + device);
return;
}
for (int i = mLogicalDisplays.size(); i-- > 0; ) {
LogicalDisplay display = mLogicalDisplays.valueAt(i);
if (display.mPrimaryDisplayDevice == device) {
final int displayId = mLogicalDisplays.keyAt(i);
display.updateFromPrimaryDisplayDevice();
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
}
}
}
}
// Runs on handler.
private void handleDisplayDeviceRemoved(DisplayDevice device) {
synchronized (mLock) {
if (!mDisplayDevices.remove(device)) {
Slog.w(TAG, "Attempted to remove non-existent display device: " + device);
return;
}
for (int i = mLogicalDisplays.size(); i-- > 0; ) {
LogicalDisplay display = mLogicalDisplays.valueAt(i);
if (display.mPrimaryDisplayDevice == device) {
final int displayId = mLogicalDisplays.keyAt(i);
mLogicalDisplays.removeAt(i);
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
}
}
}
}
// Posts a message to send a display event at the next opportunity.
private void sendDisplayEventLocked(int displayId, int event) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
mHandler.sendMessage(msg);
}
// Runs on handler.
// This method actually sends display event notifications.
// Note that it must be very careful not to be holding the lock while sending
// is in progress.
private void deliverDisplayEvent(int displayId, int event) {
if (DEBUG) {
Slog.d(TAG, "Delivering display event: displayId=" + displayId + ", event=" + event);
}
final int count;
synchronized (mLock) {
count = mCallbacks.size();
mTempCallbacks.clear();
for (int i = 0; i < count; i++) {
mTempCallbacks.add(mCallbacks.valueAt(i));
}
}
for (int i = 0; i < count; i++) {
mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);
}
mTempCallbacks.clear();
}
@Override // Binder call
public void dump(FileDescriptor fd, final 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)");
pw.println(" mHeadless=" + mHeadless);
mHandler.runWithScissors(new Runnable() {
@Override
public void run() {
dumpLocal(pw);
}
});
}
// Runs on handler.
private void dumpLocal(PrintWriter pw) {
synchronized (mLock) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
pw.println();
pw.println("Display Adapters: size=" + mDisplayAdapters.size());
for (DisplayAdapter adapter : mDisplayAdapters) {
pw.println(" " + adapter.getName());
adapter.dump(ipw);
}
pw.println();
pw.println("Display Devices: size=" + mDisplayDevices.size());
for (DisplayDevice device : mDisplayDevices) {
pw.println(" " + device);
}
final int logicalDisplayCount = mLogicalDisplays.size();
pw.println();
pw.println("Logical Displays: size=" + logicalDisplayCount);
for (int i = 0; i < logicalDisplayCount; i++) {
int displayId = mLogicalDisplays.keyAt(i);
LogicalDisplay display = mLogicalDisplays.valueAt(i);
pw.println(" Display " + displayId + ":");
pw.println(" mPrimaryDisplayDevice=" + display.mPrimaryDisplayDevice);
pw.println(" mBaseDisplayInfo=" + display.mBaseDisplayInfo);
pw.println(" mOverrideDisplayInfo="
+ display.mOverrideDisplayInfo);
}
}
}
private final class DisplayManagerHandler extends Handler {
public DisplayManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER:
registerDefaultDisplayAdapter();
break;
case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS:
registerAdditionalDisplayAdapters();
break;
case MSG_DELIVER_DISPLAY_EVENT:
deliverDisplayEvent(msg.arg1, msg.arg2);
break;
}
}
}
private final class DisplayAdapterListener implements DisplayAdapter.Listener {
@Override
public void onDisplayDeviceEvent(DisplayDevice device, int event) {
switch (event) {
case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED:
handleDisplayDeviceAdded(device);
break;
case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED:
handleDisplayDeviceChanged(device);
break;
case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED:
handleDisplayDeviceRemoved(device);
break;
}
}
}
private final class CallbackRecord implements DeathRecipient {
private final int mPid;
private final IDisplayManagerCallback mCallback;
public CallbackRecord(int pid, IDisplayManagerCallback callback) {
mPid = pid;
mCallback = callback;
}
@Override
public void binderDied() {
if (DEBUG) {
Slog.d(TAG, "Display listener for pid " + mPid + " died.");
}
onCallbackDied(mPid);
}
public void notifyDisplayEventAsync(int displayId, int event) {
try {
mCallback.onDisplayEvent(displayId, event);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify process "
+ mPid + " that displays changed, assuming it died.", ex);
binderDied();
}
}
}
/**
* Each logical display is primarily associated with one display device.
* The primary display device is nominally responsible for the basic properties
* of the logical display such as its size, refresh rate, and dpi.
*
* A logical display may be mirrored onto other display devices besides its
* primary display device, but it always remains bound to its primary.
* Note that the contents of a logical display may not always be visible, even
* on its primary display device, such as in the case where the logical display's
* primary display device is currently mirroring content from a different logical display.
*/
private final static class LogicalDisplay {
public final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
public DisplayInfo mOverrideDisplayInfo; // set by the window manager
public final DisplayDevice mPrimaryDisplayDevice;
public final DisplayDeviceInfo mPrimaryDisplayDeviceInfo = new DisplayDeviceInfo();
public LogicalDisplay(DisplayDevice primaryDisplayDevice) {
mPrimaryDisplayDevice = primaryDisplayDevice;
}
public void updateFromPrimaryDisplayDevice() {
// Bootstrap the logical display using its associated primary physical display.
mPrimaryDisplayDevice.getInfo(mPrimaryDisplayDeviceInfo);
mBaseDisplayInfo.appWidth = mPrimaryDisplayDeviceInfo.width;
mBaseDisplayInfo.appHeight = mPrimaryDisplayDeviceInfo.height;
mBaseDisplayInfo.logicalWidth = mPrimaryDisplayDeviceInfo.width;
mBaseDisplayInfo.logicalHeight = mPrimaryDisplayDeviceInfo.height;
mBaseDisplayInfo.rotation = Surface.ROTATION_0;
mBaseDisplayInfo.refreshRate = mPrimaryDisplayDeviceInfo.refreshRate;
mBaseDisplayInfo.logicalDensityDpi = mPrimaryDisplayDeviceInfo.densityDpi;
mBaseDisplayInfo.physicalXDpi = mPrimaryDisplayDeviceInfo.xDpi;
mBaseDisplayInfo.physicalYDpi = mPrimaryDisplayDeviceInfo.yDpi;
mBaseDisplayInfo.smallestNominalAppWidth = mPrimaryDisplayDeviceInfo.width;
mBaseDisplayInfo.smallestNominalAppHeight = mPrimaryDisplayDeviceInfo.height;
mBaseDisplayInfo.largestNominalAppWidth = mPrimaryDisplayDeviceInfo.width;
mBaseDisplayInfo.largestNominalAppHeight = mPrimaryDisplayDeviceInfo.height;
}
}
}