summaryrefslogtreecommitdiff
path: root/services/java/com
diff options
context:
space:
mode:
author Jeff Brown <jeffbrown@google.com> 2012-08-29 15:43:54 -0700
committer Android (Google) Code Review <android-gerrit@google.com> 2012-08-29 15:43:55 -0700
commitd5ea3b464795d4e6adbdd174d1bd2f78b628e280 (patch)
tree0660fe4a3b1da16acabfee6874051887ba056a16 /services/java/com
parent0552cbcf29f266f608e8326bc467b6afb13b7f3a (diff)
parentbd6e1500aedc5461e832f69e76341bff0e55fa2b (diff)
Merge "Add initial multi-display support." into jb-mr1-dev
Diffstat (limited to 'services/java/com')
-rw-r--r--services/java/com/android/server/SystemServer.java62
-rw-r--r--services/java/com/android/server/display/DisplayAdapter.java79
-rw-r--r--services/java/com/android/server/display/DisplayDevice.java30
-rw-r--r--services/java/com/android/server/display/DisplayDeviceInfo.java20
-rw-r--r--services/java/com/android/server/display/DisplayManagerService.java534
-rw-r--r--services/java/com/android/server/display/HeadlessDisplayAdapter.java36
-rw-r--r--services/java/com/android/server/display/LocalDisplayAdapter.java52
-rw-r--r--services/java/com/android/server/display/OverlayDisplayAdapter.java530
-rw-r--r--services/java/com/android/server/power/DisplayPowerController.java40
-rwxr-xr-xservices/java/com/android/server/wm/WindowManagerService.java150
10 files changed, 1253 insertions, 280 deletions
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7097891fd1f0..48b12155e0cf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -28,6 +28,8 @@ import android.content.pm.IPackageManager;
import android.content.res.Configuration;
import android.media.AudioService;
import android.net.wifi.p2p.WifiP2pService;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SchedulingPolicyService;
@@ -147,7 +149,52 @@ class ServerThread extends Thread {
CommonTimeManagementService commonTimeMgmtService = null;
InputManagerService inputManager = null;
+ // Create a shared handler thread for UI within the system server.
+ // This thread is used by at least the following components:
+ // - WindowManagerPolicy
+ // - KeyguardViewManager
+ // - DisplayManagerService
+ HandlerThread uiHandlerThread = new HandlerThread("UI");
+ uiHandlerThread.start();
+ Handler uiHandler = new Handler(uiHandlerThread.getLooper());
+ uiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ //Looper.myLooper().setMessageLogging(new LogPrinter(
+ // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
+ android.os.Process.setThreadPriority(
+ android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ android.os.Process.setCanSelfBackground(false);
+
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i(TAG, "Enabled StrictMode logging for UI Looper");
+ }
+ }
+ });
+
+ // Create a handler thread just for the window manager to enjoy.
+ HandlerThread wmHandlerThread = new HandlerThread("WindowManager");
+ wmHandlerThread.start();
+ Handler wmHandler = new Handler(wmHandlerThread.getLooper());
+ wmHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ //Looper.myLooper().setMessageLogging(new LogPrinter(
+ // android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM));
+ android.os.Process.setThreadPriority(
+ android.os.Process.THREAD_PRIORITY_DISPLAY);
+ android.os.Process.setCanSelfBackground(false);
+
+ // For debug builds, log event loop stalls to dropbox for analysis.
+ if (StrictMode.conditionallyEnableDebugLogging()) {
+ Slog.i(TAG, "Enabled StrictMode logging for UI Looper");
+ }
+ }
+ });
+
// Critical services...
+ boolean onlyCore = false;
try {
Slog.i(TAG, "Entropy Mixer");
ServiceManager.addService("entropy", new EntropyMixer());
@@ -160,7 +207,7 @@ class ServerThread extends Thread {
context = ActivityManagerService.main(factoryTest);
Slog.i(TAG, "Display Manager");
- display = new DisplayManagerService(context);
+ display = new DisplayManagerService(context, uiHandler);
ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
Slog.i(TAG, "Telephony Registry");
@@ -172,10 +219,14 @@ class ServerThread extends Thread {
AttributeCache.init(context);
+ if (!display.waitForDefaultDisplay()) {
+ reportWtf("Timeout waiting for default display to be initialized.",
+ new Throwable());
+ }
+
Slog.i(TAG, "Package Manager");
// Only run "core" apps if we're encrypting the device.
String cryptState = SystemProperties.get("vold.decrypt");
- boolean onlyCore = false;
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
onlyCore = true;
@@ -244,6 +295,7 @@ class ServerThread extends Thread {
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power, display,
+ uiHandler, wmHandler,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
@@ -753,6 +805,12 @@ class ServerThread extends Thread {
reportWtf("making Package Manager Service ready", e);
}
+ try {
+ display.systemReady(safeMode, onlyCore);
+ } catch (Throwable e) {
+ reportWtf("making Display Manager Service ready", e);
+ }
+
// These are needed to propagate to the runnable below.
final Context contextF = context;
final BatteryService batteryF = battery;
diff --git a/services/java/com/android/server/display/DisplayAdapter.java b/services/java/com/android/server/display/DisplayAdapter.java
index f9fa7a871f21..d19fe017d6d3 100644
--- a/services/java/com/android/server/display/DisplayAdapter.java
+++ b/services/java/com/android/server/display/DisplayAdapter.java
@@ -16,33 +16,96 @@
package com.android.server.display;
+import android.content.Context;
+import android.os.Handler;
+
+import java.io.PrintWriter;
+
/**
* A display adapter makes zero or more display devices available to the system
* and provides facilities for discovering when displays are connected or disconnected.
* <p>
* For now, all display adapters are registered in the system server but
* in principle it could be done from other processes.
+ * </p><p>
+ * Display devices are not thread-safe and must only be accessed
+ * on the display manager service's handler thread.
* </p>
*/
-public abstract class DisplayAdapter {
+public class DisplayAdapter {
+ private final Context mContext;
+ private final String mName;
+ private final Handler mHandler;
+ private Listener mListener;
+
+ public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
+ public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
+ public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3;
+
+ public DisplayAdapter(Context context, String name) {
+ mContext = context;
+ mName = name;
+ mHandler = new Handler();
+ }
+
+ public final Context getContext() {
+ return mContext;
+ }
+
+ public final Handler getHandler() {
+ return mHandler;
+ }
+
/**
* Gets the display adapter name for debugging purposes.
*
* @return The display adapter name.
*/
- public abstract String getName();
+ public final String getName() {
+ return mName;
+ }
/**
* Registers the display adapter with the display manager.
- * The display adapter should register any built-in display devices now.
- * Other display devices can be registered dynamically later.
*
- * @param listener The listener for callbacks.
+ * @param listener The listener for callbacks. The listener will
+ * be invoked on the display manager service's handler thread.
+ */
+ public final void register(Listener listener) {
+ mListener = listener;
+ onRegister();
+ }
+
+ /**
+ * Dumps the local state of the display adapter.
+ */
+ public void dump(PrintWriter pw) {
+ }
+
+ /**
+ * Called when the display adapter is registered.
+ *
+ * The display adapter should register any built-in display devices as soon as possible.
+ * The boot process will wait for the default display to be registered.
+ *
+ * Other display devices can be registered dynamically later.
*/
- public abstract void register(Listener listener);
+ protected void onRegister() {
+ }
+
+ /**
+ * Sends a display device event to the display adapter listener asynchronously.
+ */
+ protected void sendDisplayDeviceEvent(final DisplayDevice device, final int event) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayDeviceEvent(device, event);
+ }
+ });
+ }
public interface Listener {
- public void onDisplayDeviceAdded(DisplayDevice device);
- public void onDisplayDeviceRemoved(DisplayDevice device);
+ public void onDisplayDeviceEvent(DisplayDevice device, int event);
}
}
diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java
index 4a6dd66e6134..c83ce9674324 100644
--- a/services/java/com/android/server/display/DisplayDevice.java
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -21,14 +21,28 @@ import android.os.IBinder;
/**
* Represents a physical display device such as the built-in display
* an external monitor, or a WiFi display.
+ * <p>
+ * Display devices are not thread-safe and must only be accessed
+ * on the display manager service's handler thread.
+ * </p>
*/
public abstract class DisplayDevice {
+ private final DisplayAdapter mDisplayAdapter;
+ private final IBinder mDisplayToken;
+
+ public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken) {
+ mDisplayAdapter = displayAdapter;
+ mDisplayToken = displayToken;
+ }
+
/**
- * Gets the display adapter that makes the display device available.
+ * Gets the display adapter that owns the display device.
*
* @return The display adapter.
*/
- public abstract DisplayAdapter getAdapter();
+ public final DisplayAdapter getAdapter() {
+ return mDisplayAdapter;
+ }
/**
* Gets the Surface Flinger display token for this display.
@@ -36,7 +50,9 @@ public abstract class DisplayDevice {
* @return The display token, or null if the display is not being managed
* by Surface Flinger.
*/
- public abstract IBinder getDisplayToken();
+ public final IBinder getDisplayToken() {
+ return mDisplayToken;
+ }
/**
* Gets information about the display device.
@@ -44,4 +60,12 @@ public abstract class DisplayDevice {
* @param outInfo The object to populate with the information.
*/
public abstract void getInfo(DisplayDeviceInfo outInfo);
+
+ // For debugging purposes.
+ @Override
+ public String toString() {
+ DisplayDeviceInfo info = new DisplayDeviceInfo();
+ getInfo(info);
+ return info.toString() + ", owner=\"" + mDisplayAdapter.getName() + "\"";
+ }
}
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index 9c0f9644b1ed..c7b8c244e987 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -20,6 +20,9 @@ package com.android.server.display;
* Describes the characteristics of a physical display device.
*/
public final class DisplayDeviceInfo {
+ public static final int FLAG_DEFAULT_DISPLAY = 1 << 0;
+ public static final int FLAG_SECURE = 1 << 1;
+
/**
* Gets the name of the display device, which may be derived from
* EDID or other sources. The name may be displayed to the user.
@@ -43,6 +46,8 @@ public final class DisplayDeviceInfo {
public float xDpi;
public float yDpi;
+ public int flags;
+
public void copyFrom(DisplayDeviceInfo other) {
name = other.name;
width = other.width;
@@ -51,12 +56,25 @@ public final class DisplayDeviceInfo {
densityDpi = other.densityDpi;
xDpi = other.xDpi;
yDpi = other.yDpi;
+ flags = other.flags;
}
// For debugging purposes
@Override
public String toString() {
return "\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, "
- + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi";
+ + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
+ + flagsToString(flags);
+ }
+
+ private static String flagsToString(int flags) {
+ StringBuilder msg = new StringBuilder();
+ if ((flags & FLAG_DEFAULT_DISPLAY) != 0) {
+ msg.append(", FLAG_DEFAULT_DISPLAY");
+ }
+ if ((flags & FLAG_SECURE) != 0) {
+ msg.append(", FLAG_SECURE");
+ }
+ return msg.toString();
}
}
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 1463780a3289..cf835ef209b4 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -16,52 +16,151 @@
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.DisplayManager;
+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 the properties, media routing and power state of attached displays.
+ * Manages attached displays.
* <p>
- * 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.
+ * 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>();
- private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
- private DisplayDevice mDefaultDisplayDevice;
- public DisplayManagerService(Context context) {
+ // 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");
- registerDefaultDisplayAdapter();
+ 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 {
@@ -69,6 +168,34 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
}
}
+ // 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
@@ -87,95 +214,224 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
}
/**
- * Set the new display orientation.
+ * 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) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- throw new UnsupportedOperationException();
- }
-
- IBinder displayToken = mDefaultDisplayDevice.getDisplayToken();
- if (displayToken != null) {
- Surface.openTransaction();
- try {
- Surface.setDisplayOrientation(displayToken, orientation);
- } finally {
- Surface.closeTransaction();
+ // 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);
}
}
}
/**
- * Save away new DisplayInfo data.
+ * 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 setDisplayInfo(int displayId, DisplayInfo info) {
+ public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) {
synchronized (mLock) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- throw new UnsupportedOperationException();
+ 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);
}
- mDefaultDisplayInfo.copyFrom(info);
}
}
/**
- * Return requested DisplayInfo.
- * @param displayId The data to retrieve.
- * @param outInfo The structure to receive the data.
+ * 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 boolean getDisplayInfo(int displayId, DisplayInfo outInfo) {
+ public DisplayInfo getDisplayInfo(int displayId) {
synchronized (mLock) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- return false;
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ if (display.mOverrideDisplayInfo != null) {
+ return new DisplayInfo(display.mOverrideDisplayInfo);
+ }
+ return new DisplayInfo(display.mBaseDisplayInfo);
}
- outInfo.copyFrom(mDefaultDisplayInfo);
- return true;
+ return null;
}
}
- private void registerDisplayAdapter(DisplayAdapter adapter) {
- mDisplayAdapters.add(adapter);
- adapter.register(new DisplayAdapter.Listener() {
- @Override
- public void onDisplayDeviceAdded(DisplayDevice device) {
- mDefaultDisplayDevice = device;
- DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
- device.getInfo(deviceInfo);
- copyDisplayInfoFromDeviceInfo(mDefaultDisplayInfo, deviceInfo);
+ @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
- public void onDisplayDeviceRemoved(DisplayDevice device) {
+ @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 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;
+ 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, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (mContext == null
|| mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -184,19 +440,159 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
return;
}
- pw.println("DISPLAY MANAGER (dumpsys display)\n");
+ pw.println("DISPLAY MANAGER (dumpsys display)");
+ pw.println(" mHeadless=" + mHeadless);
- pw.println("Headless: " + 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: " + adapter.getName());
+ pw.println(" " + adapter.getName());
+ adapter.dump(ipw);
}
- pw.println("Default display info: " + mDefaultDisplayInfo);
+ 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*/);
}
- pw.println("Default display: "
- + DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY));
+ @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;
+ }
}
}
diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
index f984c5d90e37..f5c78b9f4930 100644
--- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java
+++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
@@ -17,52 +17,44 @@
package com.android.server.display;
import android.content.Context;
-import android.os.IBinder;
import android.util.DisplayMetrics;
/**
* Provides a fake default display for headless systems.
+ * <p>
+ * Display adapters are not thread-safe and must only be accessed
+ * on the display manager service's handler thread.
+ * </p>
*/
public final class HeadlessDisplayAdapter extends DisplayAdapter {
- private final Context mContext;
- private final HeadlessDisplayDevice mDefaultDisplayDevice;
+ private static final String TAG = "HeadlessDisplayAdapter";
public HeadlessDisplayAdapter(Context context) {
- mContext = context;
- mDefaultDisplayDevice = new HeadlessDisplayDevice();
+ super(context, TAG);
}
@Override
- public String getName() {
- return "HeadlessDisplayAdapter";
- }
-
- @Override
- public void register(Listener listener) {
- listener.onDisplayDeviceAdded(mDefaultDisplayDevice);
+ protected void onRegister() {
+ sendDisplayDeviceEvent(new HeadlessDisplayDevice(), DISPLAY_DEVICE_EVENT_ADDED);
}
private final class HeadlessDisplayDevice extends DisplayDevice {
- @Override
- public DisplayAdapter getAdapter() {
- return HeadlessDisplayAdapter.this;
- }
-
- @Override
- public IBinder getDisplayToken() {
- return null;
+ public HeadlessDisplayDevice() {
+ super(HeadlessDisplayAdapter.this, null);
}
@Override
public void getInfo(DisplayDeviceInfo outInfo) {
- outInfo.name = mContext.getResources().getString(
- com.android.internal.R.string.display_manager_built_in_display);
+ outInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_built_in_display_name);
outInfo.width = 640;
outInfo.height = 480;
outInfo.refreshRate = 60;
outInfo.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
outInfo.xDpi = 160;
outInfo.yDpi = 160;
+ outInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
+ | DisplayDeviceInfo.FLAG_SECURE;
}
}
}
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index fa778786c06b..73544fc1fa9f 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -23,58 +23,52 @@ import android.view.Surface.PhysicalDisplayInfo;
/**
* A display adapter for the local displays managed by Surface Flinger.
+ * <p>
+ * Display adapters are not thread-safe and must only be accessed
+ * on the display manager service's handler thread.
+ * </p>
*/
public final class LocalDisplayAdapter extends DisplayAdapter {
- private final Context mContext;
- private final LocalDisplayDevice mDefaultDisplayDevice;
+ private static final String TAG = "LocalDisplayAdapter";
public LocalDisplayAdapter(Context context) {
- mContext = context;
-
- IBinder token = Surface.getBuiltInDisplay(Surface.BUILT_IN_DISPLAY_ID_MAIN);
- mDefaultDisplayDevice = new LocalDisplayDevice(token);
+ super(context, TAG);
}
@Override
- public String getName() {
- return "LocalDisplayAdapter";
- }
-
- @Override
- public void register(Listener listener) {
- listener.onDisplayDeviceAdded(mDefaultDisplayDevice);
+ protected void onRegister() {
+ // TODO: listen for notifications from Surface Flinger about
+ // built-in displays being added or removed and rescan as needed.
+ IBinder displayToken = Surface.getBuiltInDisplay(Surface.BUILT_IN_DISPLAY_ID_MAIN);
+ sendDisplayDeviceEvent(new LocalDisplayDevice(displayToken, true),
+ DISPLAY_DEVICE_EVENT_ADDED);
}
private final class LocalDisplayDevice extends DisplayDevice {
- private final IBinder mDisplayToken;
-
- public LocalDisplayDevice(IBinder token) {
- mDisplayToken = token;
- }
+ private final boolean mIsDefault;
- @Override
- public DisplayAdapter getAdapter() {
- return LocalDisplayAdapter.this;
- }
-
- @Override
- public IBinder getDisplayToken() {
- return mDisplayToken;
+ public LocalDisplayDevice(IBinder displayToken, boolean isDefault) {
+ super(LocalDisplayAdapter.this, displayToken);
+ mIsDefault = isDefault;
}
@Override
public void getInfo(DisplayDeviceInfo outInfo) {
PhysicalDisplayInfo phys = new PhysicalDisplayInfo();
- Surface.getDisplayInfo(mDisplayToken, phys);
+ Surface.getDisplayInfo(getDisplayToken(), phys);
- outInfo.name = mContext.getResources().getString(
- com.android.internal.R.string.display_manager_built_in_display);
+ outInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_built_in_display_name);
outInfo.width = phys.width;
outInfo.height = phys.height;
outInfo.refreshRate = phys.refreshRate;
outInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
outInfo.xDpi = phys.xDpi;
outInfo.yDpi = phys.yDpi;
+ if (mIsDefault) {
+ outInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
+ | DisplayDeviceInfo.FLAG_SECURE;
+ }
}
}
}
diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java
new file mode 100644
index 000000000000..476d21ab08e1
--- /dev/null
+++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -0,0 +1,530 @@
+/*
+ * 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.content.Context;
+import android.database.ContentObserver;
+import android.graphics.SurfaceTexture;
+import android.hardware.display.DisplayManager;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A display adapter that uses overlay windows to simulate secondary displays
+ * for development purposes. Use Development Settings to enable one or more
+ * overlay displays.
+ * <p>
+ * Display adapters are not thread-safe and must only be accessed
+ * on the display manager service's handler thread.
+ * </p>
+ */
+public final class OverlayDisplayAdapter extends DisplayAdapter {
+ private static final String TAG = "OverlayDisplayAdapter";
+
+ private static final int MIN_WIDTH = 100;
+ private static final int MIN_HEIGHT = 100;
+ private static final int MAX_WIDTH = 4096;
+ private static final int MAX_HEIGHT = 4096;
+
+ private static final Pattern SETTING_PATTERN =
+ Pattern.compile("(\\d+)x(\\d+)/(\\d+)");
+
+ private final ArrayList<Overlay> mOverlays = new ArrayList<Overlay>();
+ private String mCurrentOverlaySetting = "";
+
+ public OverlayDisplayAdapter(Context context) {
+ super(context, TAG);
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println("mCurrentOverlaySetting=" + mCurrentOverlaySetting);
+ pw.println("mOverlays: size=" + mOverlays.size());
+ for (Overlay overlay : mOverlays) {
+ overlay.dump(pw);
+ }
+ }
+
+ @Override
+ protected void onRegister() {
+ getContext().getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.Secure.OVERLAY_DISPLAY_DEVICES), true,
+ new ContentObserver(getHandler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateOverlayDisplayDevices();
+ }
+ });
+ updateOverlayDisplayDevices();
+ }
+
+ private void updateOverlayDisplayDevices() {
+ String value = Settings.System.getString(getContext().getContentResolver(),
+ Settings.Secure.OVERLAY_DISPLAY_DEVICES);
+ if (value == null) {
+ value = "";
+ }
+
+ if (value.equals(mCurrentOverlaySetting)) {
+ return;
+ }
+ mCurrentOverlaySetting = value;
+
+ if (!mOverlays.isEmpty()) {
+ Slog.i(TAG, "Dismissing all overlay display devices.");
+ for (Overlay overlay : mOverlays) {
+ overlay.dismiss();
+ }
+ mOverlays.clear();
+ }
+
+ int number = 1;
+ for (String part : value.split(";")) {
+ if (number > 4) {
+ Slog.w(TAG, "Too many overlay display devices.");
+ }
+ Matcher matcher = SETTING_PATTERN.matcher(part);
+ if (matcher.matches()) {
+ try {
+ int width = Integer.parseInt(matcher.group(1), 10);
+ int height = Integer.parseInt(matcher.group(2), 10);
+ int densityDpi = Integer.parseInt(matcher.group(3), 10);
+ if (width >= MIN_WIDTH && width <= MAX_WIDTH
+ && height >= MIN_HEIGHT && height <= MAX_HEIGHT
+ && densityDpi >= DisplayMetrics.DENSITY_LOW
+ && densityDpi <= DisplayMetrics.DENSITY_XXHIGH) {
+ Slog.i(TAG, "Showing overlay display device #" + number
+ + ": width=" + width + ", height=" + height
+ + ", densityDpi=" + densityDpi);
+ mOverlays.add(new Overlay(number++, width, height, densityDpi));
+ continue;
+ }
+ } catch (NumberFormatException ex) {
+ }
+ } else if (part.isEmpty()) {
+ continue;
+ }
+ Slog.w(TAG, "Malformed overlay display devices setting: \"" + value + "\"");
+ }
+
+ for (Overlay overlay : mOverlays) {
+ overlay.show();
+ }
+ }
+
+ // Manages an overlay window.
+ private final class Overlay {
+ private final float INITIAL_SCALE = 0.5f;
+ private final float MIN_SCALE = 0.3f;
+ private final float MAX_SCALE = 1.0f;
+ private final float WINDOW_ALPHA = 0.8f;
+
+ // When true, disables support for moving and resizing the overlay.
+ // The window is made non-touchable, which makes it possible to
+ // directly interact with the content underneath.
+ private final boolean DISABLE_MOVE_AND_RESIZE = false;
+
+ private final DisplayManager mDisplayManager;
+ private final WindowManager mWindowManager;
+
+ private final int mNumber;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+
+ private final String mName;
+ private final String mTitle;
+
+ private final Display mDefaultDisplay;
+ private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
+ private final IBinder mDisplayToken;
+ private final OverlayDisplayDevice mDisplayDevice;
+
+ private View mWindowContent;
+ private WindowManager.LayoutParams mWindowParams;
+ private TextureView mTextureView;
+ private TextView mTitleTextView;
+ private ScaleGestureDetector mScaleGestureDetector;
+
+ private boolean mWindowVisible;
+ private int mWindowX;
+ private int mWindowY;
+ private float mWindowScale;
+
+ private int mLiveTranslationX;
+ private int mLiveTranslationY;
+ private float mLiveScale = 1.0f;
+
+ private int mDragPointerId;
+ private float mDragTouchX;
+ private float mDragTouchY;
+
+ public Overlay(int number, int width, int height, int densityDpi) {
+ Context context = getContext();
+ mDisplayManager = (DisplayManager)context.getSystemService(
+ Context.DISPLAY_SERVICE);
+ mWindowManager = (WindowManager)context.getSystemService(
+ Context.WINDOW_SERVICE);
+
+ mNumber = number;
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+
+ mName = context.getResources().getString(
+ com.android.internal.R.string.display_manager_overlay_display_name, number);
+ mTitle = context.getResources().getString(
+ com.android.internal.R.string.display_manager_overlay_display_title,
+ mNumber, mWidth, mHeight, mDensityDpi);
+
+ mDefaultDisplay = mWindowManager.getDefaultDisplay();
+ updateDefaultDisplayInfo();
+
+ mDisplayToken = Surface.createDisplay(mName);
+ mDisplayDevice = new OverlayDisplayDevice(mDisplayToken, mName,
+ mDefaultDisplayInfo.refreshRate, mDensityDpi);
+
+ createWindow();
+ }
+
+ public void show() {
+ if (!mWindowVisible) {
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ if (!updateDefaultDisplayInfo()) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ return;
+ }
+
+ clearLiveState();
+ updateWindowParams();
+ mWindowManager.addView(mWindowContent, mWindowParams);
+ mWindowVisible = true;
+ }
+ }
+
+ public void dismiss() {
+ if (mWindowVisible) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ mWindowManager.removeView(mWindowContent);
+ mWindowVisible = false;
+ }
+ }
+
+ public void relayout() {
+ if (mWindowVisible) {
+ updateWindowParams();
+ mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println(" #" + mNumber + ": "
+ + mWidth + "x" + mHeight + ", " + mDensityDpi + " dpi");
+ pw.println(" mName=" + mName);
+ pw.println(" mWindowVisible=" + mWindowVisible);
+ pw.println(" mWindowX=" + mWindowX);
+ pw.println(" mWindowY=" + mWindowY);
+ pw.println(" mWindowScale=" + mWindowScale);
+ pw.println(" mWindowParams=" + mWindowParams);
+ pw.println(" mLiveTranslationX=" + mLiveTranslationX);
+ pw.println(" mLiveTranslationY=" + mLiveTranslationY);
+ pw.println(" mLiveScale=" + mLiveScale);
+ }
+
+ private boolean updateDefaultDisplayInfo() {
+ if (!mDefaultDisplay.getDisplayInfo(mDefaultDisplayInfo)) {
+ Slog.w(TAG, "Cannot show overlay display because there is no "
+ + "default display upon which to show it.");
+ return false;
+ }
+ return true;
+ }
+
+ private void createWindow() {
+ Context context = getContext();
+ LayoutInflater inflater = LayoutInflater.from(context);
+
+ mWindowContent = inflater.inflate(
+ com.android.internal.R.layout.overlay_display_window, null);
+ mWindowContent.setOnTouchListener(mOnTouchListener);
+
+ mTextureView = (TextureView)mWindowContent.findViewById(
+ com.android.internal.R.id.overlay_display_window_texture);
+ mTextureView.setPivotX(0);
+ mTextureView.setPivotY(0);
+ mTextureView.getLayoutParams().width = mWidth;
+ mTextureView.getLayoutParams().height = mHeight;
+ mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+
+ mTitleTextView = (TextView)mWindowContent.findViewById(
+ com.android.internal.R.id.overlay_display_window_title);
+ mTitleTextView.setText(mTitle);
+
+ mWindowParams = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY);
+ mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ if (DISABLE_MOVE_AND_RESIZE) {
+ mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ }
+ mWindowParams.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
+ mWindowParams.alpha = WINDOW_ALPHA;
+ mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
+ mWindowParams.setTitle(mTitle);
+
+ mScaleGestureDetector = new ScaleGestureDetector(context, mOnScaleGestureListener);
+
+ // By default, arrange the displays in the four corners.
+ mWindowVisible = false;
+ mWindowScale = INITIAL_SCALE;
+ if (mNumber == 2 || mNumber == 3) {
+ mWindowX = mDefaultDisplayInfo.logicalWidth;
+ } else {
+ mWindowX = 0;
+ }
+ if (mNumber == 2 || mNumber == 4) {
+ mWindowY = mDefaultDisplayInfo.logicalHeight;
+ } else {
+ mWindowY = 0;
+ }
+ }
+
+ private void updateWindowParams() {
+ float scale = mWindowScale * mLiveScale;
+ if (mWidth * scale > mDefaultDisplayInfo.logicalWidth) {
+ scale = mDefaultDisplayInfo.logicalWidth / mWidth;
+ }
+ if (mHeight * scale > mDefaultDisplayInfo.logicalHeight) {
+ scale = mDefaultDisplayInfo.logicalHeight / mHeight;
+ }
+ scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
+
+ float offsetScale = (scale / mWindowScale - 1.0f) * 0.5f;
+ int width = (int)(mWidth * scale);
+ int height = (int)(mHeight * scale);
+ int x = mWindowX + mLiveTranslationX - (int)(width * offsetScale);
+ int y = mWindowY + mLiveTranslationY - (int)(height * offsetScale);
+ x = Math.max(0, Math.min(x, mDefaultDisplayInfo.logicalWidth - width));
+ y = Math.max(0, Math.min(y, mDefaultDisplayInfo.logicalHeight - height));
+
+ mTextureView.setScaleX(scale);
+ mTextureView.setScaleY(scale);
+
+ mWindowParams.x = x;
+ mWindowParams.y = y;
+ mWindowParams.width = width;
+ mWindowParams.height = height;
+ }
+
+ private void saveWindowParams() {
+ mWindowX = mWindowParams.x;
+ mWindowY = mWindowParams.y;
+ mWindowScale = mTextureView.getScaleX();
+ clearLiveState();
+ }
+
+ private void clearLiveState() {
+ mLiveTranslationX = 0;
+ mLiveTranslationY = 0;
+ mLiveScale = 1.0f;
+ }
+
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == mDefaultDisplay.getDisplayId()) {
+ if (updateDefaultDisplayInfo()) {
+ relayout();
+ } else {
+ dismiss();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (displayId == mDefaultDisplay.getDisplayId()) {
+ dismiss();
+ }
+ }
+ };
+
+ private final SurfaceTextureListener mSurfaceTextureListener =
+ new SurfaceTextureListener() {
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ Surface.openTransaction();
+ try {
+ Surface.setDisplaySurface(mDisplayToken, surface);
+ } finally {
+ Surface.closeTransaction();
+ }
+
+ mDisplayDevice.setSize(width, height);
+ sendDisplayDeviceEvent(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ sendDisplayDeviceEvent(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
+
+ Surface.openTransaction();
+ try {
+ Surface.setDisplaySurface(mDisplayToken, null);
+ } finally {
+ Surface.closeTransaction();
+ }
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ mDisplayDevice.setSize(width, height);
+ sendDisplayDeviceEvent(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ }
+ };
+
+ private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ // Work in screen coordinates.
+ final float oldX = event.getX();
+ final float oldY = event.getY();
+ event.setLocation(event.getRawX(), event.getRawY());
+
+ mScaleGestureDetector.onTouchEvent(event);
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ resetDrag(event);
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (event.getPointerCount() == 1) {
+ int index = event.findPointerIndex(mDragPointerId);
+ if (index < 0) {
+ resetDrag(event);
+ } else {
+ mLiveTranslationX = (int)(event.getX(index) - mDragTouchX);
+ mLiveTranslationY = (int)(event.getY(index) - mDragTouchY);
+ relayout();
+ }
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ saveWindowParams();
+ break;
+ }
+
+ // Revert to window coordinates.
+ event.setLocation(oldX, oldY);
+ return true;
+ }
+
+ private void resetDrag(MotionEvent event) {
+ saveWindowParams();
+ mDragPointerId = event.getPointerId(0);
+ mDragTouchX = event.getX();
+ mDragTouchY = event.getY();
+ }
+ };
+
+ private final ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener =
+ new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ saveWindowParams();
+ mDragPointerId = -1; // cause drag to be reset
+ return true;
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ mLiveScale = detector.getScaleFactor();
+ relayout();
+ return false;
+ }
+ };
+ }
+
+ private final class OverlayDisplayDevice extends DisplayDevice {
+ private final String mName;
+ private final float mRefreshRate;
+ private final int mDensityDpi;
+ private int mWidth;
+ private int mHeight;
+
+ public OverlayDisplayDevice(IBinder displayToken, String name,
+ float refreshRate, int densityDpi) {
+ super(OverlayDisplayAdapter.this, displayToken);
+ mName = name;
+ mRefreshRate = refreshRate;
+ mDensityDpi = densityDpi;
+ }
+
+ public void setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ @Override
+ public void getInfo(DisplayDeviceInfo outInfo) {
+ outInfo.name = mName;
+ outInfo.width = mWidth;
+ outInfo.height = mHeight;
+ outInfo.refreshRate = mRefreshRate;
+ outInfo.densityDpi = mDensityDpi;
+ outInfo.xDpi = mDensityDpi;
+ outInfo.yDpi = mDensityDpi;
+ outInfo.flags = DisplayDeviceInfo.FLAG_SECURE;
+ }
+ }
+}
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index cd211da09973..6b6d8994eec6 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -45,7 +45,6 @@ import android.view.Display;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
/**
@@ -169,6 +168,9 @@ final class DisplayPowerController {
// The twilight service.
private final TwilightService mTwilight;
+ // The display manager.
+ private final DisplayManager mDisplayManager;
+
// The sensor manager.
private final SensorManager mSensorManager;
@@ -330,6 +332,7 @@ final class DisplayPowerController {
mLights = lights;
mTwilight = twilight;
mSensorManager = new SystemSensorManager(mHandler.getLooper());
+ mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
final Resources resources = context.getResources();
mScreenBrightnessDimConfig = resources.getInteger(
@@ -475,7 +478,7 @@ final class DisplayPowerController {
private void initialize() {
final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR;
- Display display = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
+ Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
mPowerState = new DisplayPowerState(new ElectronBeam(display),
new PhotonicModulator(executor,
mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
@@ -980,7 +983,7 @@ final class DisplayPowerController {
}
};
- public void dump(PrintWriter pw) {
+ public void dump(final PrintWriter pw) {
synchronized (mLock) {
pw.println();
pw.println("Display Controller Locked State:");
@@ -1000,33 +1003,12 @@ final class DisplayPowerController {
pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
- if (Looper.myLooper() == mHandler.getLooper()) {
- dumpLocal(pw);
- } else {
- final StringWriter out = new StringWriter();
- final CountDownLatch latch = new CountDownLatch(1);
- Message msg = Message.obtain(mHandler, new Runnable() {
- @Override
- public void run() {
- PrintWriter localpw = new PrintWriter(out);
- try {
- dumpLocal(localpw);
- } finally {
- localpw.flush();
- latch.countDown();
- }
- }
- });
- msg.setAsynchronous(true);
- mHandler.sendMessage(msg);
- try {
- latch.await();
- pw.print(out.toString());
- } catch (InterruptedException ex) {
- pw.println();
- pw.println("Failed to dump thread state due to interrupted exception!");
+ mHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ dumpLocal(pw);
}
- }
+ });
}
private void dumpLocal(PrintWriter pw) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index c1047a98d033..0d9db900c393 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -739,116 +739,38 @@ public class WindowManagerService extends IWindowManager.Stub
// For example, when this flag is true, there will be no wallpaper service.
final boolean mOnlyCore;
- public static WindowManagerService main(Context context,
- PowerManagerService pm, DisplayManagerService dm,
- boolean haveInputMethods, boolean allowBootMsgs,
- boolean onlyCore) {
- WMThread thr = new WMThread(context, pm, dm, haveInputMethods, allowBootMsgs, onlyCore);
- thr.start();
-
- synchronized (thr) {
- while (thr.mService == null) {
- try {
- thr.wait();
- } catch (InterruptedException e) {
- }
+ public static WindowManagerService main(final Context context,
+ final PowerManagerService pm, final DisplayManagerService dm,
+ final Handler uiHandler, final Handler wmHandler,
+ final boolean haveInputMethods, final boolean showBootMsgs,
+ final boolean onlyCore) {
+ final WindowManagerService[] holder = new WindowManagerService[1];
+ wmHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ holder[0] = new WindowManagerService(context, pm, dm,
+ uiHandler, haveInputMethods, showBootMsgs, onlyCore);
}
- return thr.mService;
- }
- }
-
- static class WMThread extends Thread {
- WindowManagerService mService;
-
- private final Context mContext;
- private final PowerManagerService mPM;
- private final DisplayManagerService mDisplayManager;
- private final boolean mHaveInputMethods;
- private final boolean mAllowBootMessages;
- private final boolean mOnlyCore;
-
- public WMThread(Context context, PowerManagerService pm,
- DisplayManagerService dm,
- boolean haveInputMethods, boolean allowBootMsgs, boolean onlyCore) {
- super("WindowManager");
- mContext = context;
- mPM = pm;
- mDisplayManager = dm;
- mHaveInputMethods = haveInputMethods;
- mAllowBootMessages = allowBootMsgs;
- mOnlyCore = onlyCore;
- }
-
- @Override
- public void run() {
- Looper.prepare();
- //Looper.myLooper().setMessageLogging(new LogPrinter(
- // android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM));
- WindowManagerService s = new WindowManagerService(mContext, mPM, mDisplayManager,
- mHaveInputMethods, mAllowBootMessages, mOnlyCore);
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_DISPLAY);
- android.os.Process.setCanSelfBackground(false);
-
- synchronized (this) {
- mService = s;
- notifyAll();
- }
-
- // For debug builds, log event loop stalls to dropbox for analysis.
- if (StrictMode.conditionallyEnableDebugLogging()) {
- Slog.i(TAG, "Enabled StrictMode logging for WMThread's Looper");
- }
-
- Looper.loop();
- }
+ });
+ return holder[0];
}
- static class PolicyThread extends Thread {
- private final WindowManagerPolicy mPolicy;
- private final WindowManagerService mService;
- private final Context mContext;
- boolean mRunning = false;
+ private void initPolicy(Handler uiHandler) {
+ uiHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
- public PolicyThread(WindowManagerPolicy policy,
- WindowManagerService service, Context context) {
- super("WindowManagerPolicy");
- mPolicy = policy;
- mService = service;
- mContext = context;
- }
-
- @Override
- public void run() {
- Looper.prepare();
- WindowManagerPolicyThread.set(this, Looper.myLooper());
-
- //Looper.myLooper().setMessageLogging(new LogPrinter(
- // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_FOREGROUND);
- android.os.Process.setCanSelfBackground(false);
- mPolicy.init(mContext, mService, mService);
- mService.mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer()
- * TYPE_LAYER_MULTIPLIER
- + TYPE_LAYER_OFFSET;
-
- synchronized (this) {
- mRunning = true;
- notifyAll();
- }
-
- // For debug builds, log event loop stalls to dropbox for analysis.
- if (StrictMode.conditionallyEnableDebugLogging()) {
- Slog.i(TAG, "Enabled StrictMode for PolicyThread's Looper");
+ mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
+ mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer()
+ * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
}
-
- Looper.loop();
- }
+ });
}
private WindowManagerService(Context context, PowerManagerService pm,
- DisplayManagerService displayManager,
+ DisplayManagerService displayManager, Handler uiHandler,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
mContext = context;
mHaveInputMethods = haveInputMethods;
@@ -857,7 +779,7 @@ public class WindowManagerService extends IWindowManager.Stub
mLimitedAlphaCompositing = context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_limitedAlpha);
mDisplayManagerService = displayManager;
- mDisplayManager = DisplayManager.getInstance();
+ mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mHeadless = displayManager.isHeadless();
mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
@@ -895,17 +817,7 @@ public class WindowManagerService extends IWindowManager.Stub
mFxSession = new SurfaceSession();
mAnimator = new WindowAnimator(this);
- PolicyThread thr = new PolicyThread(mPolicy, this, context);
- thr.start();
-
- synchronized (thr) {
- while (!thr.mRunning) {
- try {
- thr.wait();
- } catch (InterruptedException e) {
- }
- }
- }
+ initPolicy(uiHandler);
mInputManager.start();
@@ -6562,7 +6474,8 @@ public class WindowManagerService extends IWindowManager.Stub
displayInfo.appHeight = appHeight;
displayInfo.getLogicalMetrics(mRealDisplayMetrics, null);
displayInfo.getAppMetrics(mDisplayMetrics, null);
- mDisplayManagerService.setDisplayInfo(displayContent.getDisplayId(), displayInfo);
+ mDisplayManagerService.setDisplayInfoOverrideFromWindowManager(
+ displayContent.getDisplayId(), displayInfo);
mAnimator.setDisplayDimensions(dw, dh, appWidth, appHeight);
}
@@ -6914,7 +6827,10 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(displayContent.mDisplaySizeLock) {
// Bootstrap the default logical display from the display manager.
displayInfo = displayContent.getDisplayInfo();
- mDisplayManagerService.getDisplayInfo(displayId, displayInfo);
+ DisplayInfo newDisplayInfo = mDisplayManagerService.getDisplayInfo(displayId);
+ if (newDisplayInfo != null) {
+ displayInfo.copyFrom(newDisplayInfo);
+ }
displayContent.mInitialDisplayWidth = displayInfo.logicalWidth;
displayContent.mInitialDisplayHeight = displayInfo.logicalHeight;
displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi;
@@ -10365,7 +10281,7 @@ public class WindowManagerService extends IWindowManager.Stub
public DisplayContent getDisplayContent(final int displayId) {
DisplayContent displayContent = mDisplayContents.get(displayId);
if (displayContent == null) {
- displayContent = new DisplayContent(mDisplayManager.getRealDisplay(displayId));
+ displayContent = new DisplayContent(mDisplayManager.getDisplay(displayId));
mDisplayContents.put(displayId, displayContent);
}
return displayContent;