diff --git a/Android.mk b/Android.mk index 0665e60cb0071..a637eb74816d0 100644 --- a/Android.mk +++ b/Android.mk @@ -115,6 +115,7 @@ LOCAL_SRC_FILES += \ core/java/android/database/IContentObserver.aidl \ core/java/android/hardware/ISerialManager.aidl \ core/java/android/hardware/display/IDisplayManager.aidl \ + core/java/android/hardware/display/IDisplayManagerCallback.aidl \ core/java/android/hardware/input/IInputManager.aidl \ core/java/android/hardware/input/IInputDevicesChangedListener.aidl \ core/java/android/hardware/usb/IUsbManager.aidl \ diff --git a/api/current.txt b/api/current.txt index 14928a0357cb2..f5e396667bcba 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9997,7 +9997,8 @@ package android.hardware { package android.hardware.display { public final class DisplayManager { - method public android.view.Display getDisplay(int, android.content.Context); + method public android.view.Display getDisplay(int); + method public android.view.Display[] getDisplays(); method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler); method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener); } @@ -23453,6 +23454,7 @@ package android.view { method public int getRotation(); method public void getSize(android.graphics.Point); method public deprecated int getWidth(); + method public boolean isValid(); field public static final int DEFAULT_DISPLAY = 0; // 0x0 } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index e644db46e2959..f8a9d75f2e30a 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -31,6 +31,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Point; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.os.Binder; import android.os.Bundle; import android.os.Debug; @@ -376,7 +377,8 @@ public class ActivityManager { return true; } - Display display = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); + Display display = DisplayManagerGlobal.getInstance().getRealDisplay( + Display.DEFAULT_DISPLAY); Point p = new Point(); display.getRealSize(p); int pixels = p.x * p.y; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b8e16c5a7cc93..4a1bf7523dd7c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -43,6 +43,7 @@ import android.database.sqlite.SQLiteDebug.DbStats; import android.graphics.Bitmap; import android.graphics.Canvas; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.net.IConnectivityManager; import android.net.Proxy; import android.net.ProxyProperties; @@ -1557,7 +1558,7 @@ public final class ActivityThread { return dm; } - DisplayManager displayManager = DisplayManager.getInstance(); + DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance(); if (displayManager == null) { // may be null early in system startup dm = new DisplayMetrics(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 32086d7363570..efe4b7b4e0173 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -349,10 +349,11 @@ class ContextImpl extends Context { return InputManager.getInstance(); }}); - registerService(DISPLAY_SERVICE, new StaticServiceFetcher() { - public Object createStaticService() { - return DisplayManager.getInstance(); - }}); + registerService(DISPLAY_SERVICE, new ServiceFetcher() { + @Override + public Object createService(ContextImpl ctx) { + return new DisplayManager(ctx.getOuterContext()); + }}); registerService(INPUT_METHOD_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 98d2f69ee2269..74996da1ed1b1 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -18,20 +18,12 @@ package android.hardware.display; import android.content.Context; import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.util.Log; +import android.util.SparseArray; import android.view.CompatibilityInfoHolder; import android.view.Display; -import android.view.DisplayInfo; - -import java.util.ArrayList; /** - * Manages the properties, media routing and power state of attached displays. + * Manages the properties of attached displays. *

* Get an instance of this class by calling * {@link android.content.Context#getSystemService(java.lang.String) @@ -43,110 +35,79 @@ public final class DisplayManager { private static final String TAG = "DisplayManager"; private static final boolean DEBUG = false; - private static final int MSG_DISPLAY_ADDED = 1; - private static final int MSG_DISPLAY_REMOVED = 2; - private static final int MSG_DISPLAY_CHANGED = 3; + private final Context mContext; + private final DisplayManagerGlobal mGlobal; - private static DisplayManager sInstance; + private final Object mLock = new Object(); + private final SparseArray mDisplays = new SparseArray(); - private final IDisplayManager mDm; - - // Guarded by mDisplayLock - private final Object mDisplayLock = new Object(); - private final ArrayList mDisplayListeners = - new ArrayList(); - - - private DisplayManager(IDisplayManager dm) { - mDm = dm; + /** @hide */ + public DisplayManager(Context context) { + mContext = context; + mGlobal = DisplayManagerGlobal.getInstance(); } /** - * Gets an instance of the display manager. + * Gets information about a logical display. * - * @return The display manager instance, may be null early in system startup - * before the display manager has been fully initialized. + * The display metrics may be adjusted to provide compatibility + * for legacy applications. * - * @hide + * @param displayId The logical display id. + * @return The display object, or null if there is no valid display with the given id. */ - public static DisplayManager getInstance() { - synchronized (DisplayManager.class) { - if (sInstance == null) { - IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE); - if (b != null) { - sInstance = new DisplayManager(IDisplayManager.Stub.asInterface(b)); + public Display getDisplay(int displayId) { + synchronized (mLock) { + return getOrCreateDisplayLocked(displayId, false /*assumeValid*/); + } + } + + /** + * Gets all currently valid logical displays. + * + * @return An array containing all displays. + */ + public Display[] getDisplays() { + int[] displayIds = mGlobal.getDisplayIds(); + int expectedCount = displayIds.length; + Display[] displays = new Display[expectedCount]; + synchronized (mLock) { + int actualCount = 0; + for (int i = 0; i < expectedCount; i++) { + Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/); + if (display != null) { + displays[actualCount++] = display; } } - return sInstance; + if (actualCount != expectedCount) { + Display[] oldDisplays = displays; + displays = new Display[actualCount]; + System.arraycopy(oldDisplays, 0, displays, 0, actualCount); + } } + return displays; } - /** - * Get information about a particular logical display. - * - * @param displayId The logical display id. - * @param outInfo A structure to populate with the display info. - * @return True if the logical display exists, false otherwise. - * @hide - */ - public boolean getDisplayInfo(int displayId, DisplayInfo outInfo) { - try { - return mDm.getDisplayInfo(displayId, outInfo); - } catch (RemoteException ex) { - Log.e(TAG, "Could not get display information from display manager.", ex); - return false; + private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) { + Display display = mDisplays.get(displayId); + if (display == null) { + display = mGlobal.getCompatibleDisplay(displayId, + getCompatibilityInfoForDisplayLocked(displayId)); + if (display != null) { + mDisplays.put(displayId, display); + } + } else if (!assumeValid && !display.isValid()) { + display = null; } + return display; } - /** - * Gets information about a logical display. - * - * The display metrics may be adjusted to provide compatibility - * for legacy applications. - * - * @param displayId The logical display id. - * @param applicationContext The application context from which to obtain - * compatible metrics. - * @return The display object. - */ - public Display getDisplay(int displayId, Context applicationContext) { - if (applicationContext == null) { - throw new IllegalArgumentException("applicationContext must not be null"); - } - + private CompatibilityInfoHolder getCompatibilityInfoForDisplayLocked(int displayId) { CompatibilityInfoHolder cih = null; if (displayId == Display.DEFAULT_DISPLAY) { - cih = applicationContext.getCompatibilityInfo(); + cih = mContext.getCompatibilityInfo(); } - return getCompatibleDisplay(displayId, cih); - } - - /** - * Gets information about a logical display. - * - * The display metrics may be adjusted to provide compatibility - * for legacy applications. - * - * @param displayId The logical display id. - * @param cih The compatibility info, or null if none is required. - * @return The display object. - * - * @hide - */ - public Display getCompatibleDisplay(int displayId, CompatibilityInfoHolder cih) { - return new Display(displayId, cih); - } - - /** - * Gets information about a logical display without applying any compatibility metrics. - * - * @param displayId The logical display id. - * @return The display object. - * - * @hide - */ - public Display getRealDisplay(int displayId) { - return getCompatibleDisplay(displayId, null); + return cih; } /** @@ -160,16 +121,7 @@ public final class DisplayManager { * @see #unregisterDisplayListener */ public void registerDisplayListener(DisplayListener listener, Handler handler) { - if (listener == null) { - throw new IllegalArgumentException("listener must not be null"); - } - - synchronized (mDisplayLock) { - int index = findDisplayListenerLocked(listener); - if (index < 0) { - mDisplayListeners.add(new DisplayListenerDelegate(listener, handler)); - } - } + mGlobal.registerDisplayListener(listener, handler); } /** @@ -180,28 +132,7 @@ public final class DisplayManager { * @see #registerDisplayListener */ public void unregisterDisplayListener(DisplayListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener must not be null"); - } - - synchronized (mDisplayLock) { - int index = findDisplayListenerLocked(listener); - if (index >= 0) { - DisplayListenerDelegate d = mDisplayListeners.get(index); - d.removeCallbacksAndMessages(null); - mDisplayListeners.remove(index); - } - } - } - - private int findDisplayListenerLocked(DisplayListener listener) { - final int numListeners = mDisplayListeners.size(); - for (int i = 0; i < numListeners; i++) { - if (mDisplayListeners.get(i).mListener == listener) { - return i; - } - } - return -1; + mGlobal.unregisterDisplayListener(listener); } /** @@ -210,7 +141,8 @@ public final class DisplayManager { public interface DisplayListener { /** * Called whenever a logical display has been added to the system. - * Use {@link DisplayManager#getDisplay} to get more information about the display. + * Use {@link DisplayManager#getDisplay} to get more information about + * the display. * * @param displayId The id of the logical display that was added. */ @@ -230,28 +162,4 @@ public final class DisplayManager { */ void onDisplayChanged(int displayId); } - - private static final class DisplayListenerDelegate extends Handler { - public final DisplayListener mListener; - - public DisplayListenerDelegate(DisplayListener listener, Handler handler) { - super(handler != null ? handler.getLooper() : Looper.myLooper()); - mListener = listener; - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_DISPLAY_ADDED: - mListener.onDisplayAdded(msg.arg1); - break; - case MSG_DISPLAY_REMOVED: - mListener.onDisplayRemoved(msg.arg1); - break; - case MSG_DISPLAY_CHANGED: - mListener.onDisplayChanged(msg.arg1); - break; - } - } - } } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java new file mode 100644 index 0000000000000..69c0319dc7bf6 --- /dev/null +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -0,0 +1,273 @@ +/* + * 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 android.hardware.display; + +import android.content.Context; +import android.hardware.display.DisplayManager.DisplayListener; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import android.util.SparseArray; +import android.view.CompatibilityInfoHolder; +import android.view.Display; +import android.view.DisplayInfo; + +import java.util.ArrayList; + +/** + * Manager communication with the display manager service on behalf of + * an application process. You're probably looking for {@link DisplayManager}. + * + * @hide + */ +public final class DisplayManagerGlobal { + private static final String TAG = "DisplayManager"; + private static final boolean DEBUG = false; + + public static final int EVENT_DISPLAY_ADDED = 1; + public static final int EVENT_DISPLAY_CHANGED = 2; + public static final int EVENT_DISPLAY_REMOVED = 3; + + private static DisplayManagerGlobal sInstance; + + private final Object mLock = new Object(); + + private final IDisplayManager mDm; + + private DisplayManagerCallback mCallback; + private final ArrayList mDisplayListeners = + new ArrayList(); + + private final SparseArray mDisplayInfoCache = new SparseArray(); + private int[] mDisplayIdCache; + + private DisplayManagerGlobal(IDisplayManager dm) { + mDm = dm; + } + + /** + * Gets an instance of the display manager global singleton. + * + * @return The display manager instance, may be null early in system startup + * before the display manager has been fully initialized. + */ + public static DisplayManagerGlobal getInstance() { + synchronized (DisplayManagerGlobal.class) { + if (sInstance == null) { + IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE); + if (b != null) { + sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b)); + } + } + return sInstance; + } + } + + /** + * Get information about a particular logical display. + * + * @param displayId The logical display id. + * @return Information about the specified display, or null if it does not exist. + * This object belongs to an internal cache and should be treated as if it were immutable. + */ + public DisplayInfo getDisplayInfo(int displayId) { + try { + synchronized (mLock) { + DisplayInfo info = mDisplayInfoCache.get(displayId); + if (info != null) { + return info; + } + + info = mDm.getDisplayInfo(displayId); + if (info == null) { + return null; + } + if (DEBUG) { + Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info); + } + + mDisplayInfoCache.put(displayId, info); + registerCallbackIfNeededLocked(); + return info; + } + } catch (RemoteException ex) { + Log.e(TAG, "Could not get display information from display manager.", ex); + return null; + } + } + + /** + * Gets all currently valid logical display ids. + * + * @return An array containing all display ids. + */ + public int[] getDisplayIds() { + try { + synchronized (mLock) { + if (mDisplayIdCache == null) { + mDisplayIdCache = mDm.getDisplayIds(); + registerCallbackIfNeededLocked(); + } + return mDisplayIdCache; + } + } catch (RemoteException ex) { + Log.e(TAG, "Could not get display ids from display manager.", ex); + return new int[] { Display.DEFAULT_DISPLAY }; + } + } + + /** + * Gets information about a logical display. + * + * The display metrics may be adjusted to provide compatibility + * for legacy applications. + * + * @param displayId The logical display id. + * @param cih The compatibility info, or null if none is required. + * @return The display object, or null if there is no display with the given id. + */ + public Display getCompatibleDisplay(int displayId, CompatibilityInfoHolder cih) { + DisplayInfo displayInfo = getDisplayInfo(displayId); + if (displayInfo == null) { + return null; + } + return new Display(this, displayId, displayInfo, cih); + } + + /** + * Gets information about a logical display without applying any compatibility metrics. + * + * @param displayId The logical display id. + * @return The display object, or null if there is no display with the given id. + */ + public Display getRealDisplay(int displayId) { + return getCompatibleDisplay(displayId, null); + } + + public void registerDisplayListener(DisplayListener listener, Handler handler) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mLock) { + int index = findDisplayListenerLocked(listener); + if (index < 0) { + mDisplayListeners.add(new DisplayListenerDelegate(listener, handler)); + registerCallbackIfNeededLocked(); + } + } + } + + public void unregisterDisplayListener(DisplayListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mLock) { + int index = findDisplayListenerLocked(listener); + if (index >= 0) { + DisplayListenerDelegate d = mDisplayListeners.get(index); + d.clearEvents(); + mDisplayListeners.remove(index); + } + } + } + + private int findDisplayListenerLocked(DisplayListener listener) { + final int numListeners = mDisplayListeners.size(); + for (int i = 0; i < numListeners; i++) { + if (mDisplayListeners.get(i).mListener == listener) { + return i; + } + } + return -1; + } + + private void registerCallbackIfNeededLocked() { + if (mCallback == null) { + mCallback = new DisplayManagerCallback(); + try { + mDm.registerCallback(mCallback); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to register callback with display manager service.", ex); + mCallback = null; + } + } + } + + private void handleDisplayEvent(int displayId, int event) { + synchronized (mLock) { + mDisplayInfoCache.remove(displayId); + + if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) { + mDisplayIdCache = null; + } + + final int numListeners = mDisplayListeners.size(); + for (int i = 0; i < numListeners; i++) { + mDisplayListeners.get(i).sendDisplayEvent(displayId, event); + } + } + } + + private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { + @Override + public void onDisplayEvent(int displayId, int event) { + if (DEBUG) { + Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); + } + handleDisplayEvent(displayId, event); + } + } + + private static final class DisplayListenerDelegate extends Handler { + public final DisplayListener mListener; + + public DisplayListenerDelegate(DisplayListener listener, Handler handler) { + super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); + mListener = listener; + } + + public void sendDisplayEvent(int displayId, int event) { + Message msg = obtainMessage(event, displayId, 0); + sendMessage(msg); + } + + public void clearEvents() { + removeCallbacksAndMessages(null); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_DISPLAY_ADDED: + mListener.onDisplayAdded(msg.arg1); + break; + case EVENT_DISPLAY_CHANGED: + mListener.onDisplayChanged(msg.arg1); + break; + case EVENT_DISPLAY_REMOVED: + mListener.onDisplayRemoved(msg.arg1); + break; + } + } + } +} diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index fd8c35f8d614e..d802aa16d6a5b 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -16,9 +16,13 @@ package android.hardware.display; +import android.hardware.display.IDisplayManagerCallback; import android.view.DisplayInfo; /** @hide */ interface IDisplayManager { - boolean getDisplayInfo(int displayId, out DisplayInfo outInfo); + DisplayInfo getDisplayInfo(int displayId); + int[] getDisplayIds(); + + void registerCallback(in IDisplayManagerCallback callback); } diff --git a/core/java/android/hardware/display/IDisplayManagerCallback.aidl b/core/java/android/hardware/display/IDisplayManagerCallback.aidl new file mode 100644 index 0000000000000..c50e3fb261563 --- /dev/null +++ b/core/java/android/hardware/display/IDisplayManagerCallback.aidl @@ -0,0 +1,22 @@ +/* + * 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 android.hardware.display; + +/** @hide */ +interface IDisplayManagerCallback { + oneway void onDisplayEvent(int displayId, int event); +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6f43891a88b68..b4841b18bb778 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4350,6 +4350,25 @@ public final class Settings { */ public static final String SMS_SHORT_CODES_PREFIX = "sms_short_codes_"; + /** + * Overlay display devices setting. + * The associated value is a specially formatted string that describes the + * size and density of simulated secondary display devices. + *

+ * Format: {width}x{height}/{dpi};... + *

+ * Example: + *

+ * + * @hide + */ + public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices"; + /** * This are the settings to be backed up. * diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 392d1f2dd19ed..6848606c758aa 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -17,6 +17,7 @@ package android.view; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -166,8 +167,7 @@ public final class Choreographer { mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null; mLastFrameTimeNanos = Long.MIN_VALUE; - Display d = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); - mFrameIntervalNanos = (long)(1000000000 / d.getRefreshRate()); + mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { @@ -175,6 +175,12 @@ public final class Choreographer { } } + private static float getRefreshRate() { + DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo( + Display.DEFAULT_DISPLAY); + return di.refreshRate; + } + /** * Gets the choreographer for the calling thread. Must be called from * a thread that already has a {@link android.os.Looper} associated with it. diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 6f8ca13f1c761..ec635a2156fea 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -19,7 +19,7 @@ package android.view; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; -import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.os.SystemClock; import android.util.DisplayMetrics; import android.util.Log; @@ -49,10 +49,14 @@ import android.util.Log; */ public final class Display { private static final String TAG = "Display"; + private static final boolean DEBUG = false; + private final DisplayManagerGlobal mGlobal; private final int mDisplayId; private final CompatibilityInfoHolder mCompatibilityInfo; - private final DisplayInfo mDisplayInfo = new DisplayInfo(); + + private DisplayInfo mDisplayInfo; // never null + private boolean mIsValid; // Temporary display metrics structure used for compatibility mode. private final DisplayMetrics mTempMetrics = new DisplayMetrics(); @@ -80,9 +84,14 @@ public final class Display { * * @hide */ - public Display(int displayId, CompatibilityInfoHolder compatibilityInfo) { + public Display(DisplayManagerGlobal global, + int displayId, DisplayInfo displayInfo /*not null*/, + CompatibilityInfoHolder compatibilityInfo) { + mGlobal = global; mDisplayId = displayId; + mDisplayInfo = displayInfo; mCompatibilityInfo = compatibilityInfo; + mIsValid = true; } /** @@ -96,16 +105,38 @@ public final class Display { return mDisplayId; } + /** + * Returns true if this display is still valid, false if the display has been removed. + * + * If the display is invalid, then the methods of this class will + * continue to report the most recently observed display information. + * However, it is unwise (and rather fruitless) to continue using a + * {@link Display} object after the display's demise. + * + * It's possible for a display that was previously invalid to become + * valid again if a display with the same id is reconnected. + * + * @return True if the display is still valid. + */ + public boolean isValid() { + synchronized (this) { + updateDisplayInfoLocked(); + return mIsValid; + } + } + /** * Gets a full copy of the display information. * * @param outDisplayInfo The object to receive the copy of the display information. + * @return True if the display is still valid. * @hide */ - public void getDisplayInfo(DisplayInfo outDisplayInfo) { + public boolean getDisplayInfo(DisplayInfo outDisplayInfo) { synchronized (this) { updateDisplayInfoLocked(); outDisplayInfo.copyFrom(mDisplayInfo); + return mIsValid; } } @@ -366,9 +397,25 @@ public final class Display { } private void updateDisplayInfoLocked() { - // TODO: only refresh the display information when needed - if (!DisplayManager.getInstance().getDisplayInfo(mDisplayId, mDisplayInfo)) { - Log.e(TAG, "Could not get information about logical display " + mDisplayId); + // Note: The display manager caches display info objects on our behalf. + DisplayInfo newInfo = mGlobal.getDisplayInfo(mDisplayId); + if (newInfo == null) { + // Preserve the old mDisplayInfo after the display is removed. + if (mIsValid) { + mIsValid = false; + if (DEBUG) { + Log.d(TAG, "Logical display " + mDisplayId + " was removed."); + } + } + } else { + // Use the new display info. (It might be the same object if nothing changed.) + mDisplayInfo = newInfo; + if (!mIsValid) { + mIsValid = true; + if (DEBUG) { + Log.d(TAG, "Logical display " + mDisplayId + " was recreated."); + } + } } } @@ -390,7 +437,7 @@ public final class Display { updateDisplayInfoLocked(); mDisplayInfo.getAppMetrics(mTempMetrics, mCompatibilityInfo); return "Display id " + mDisplayId + ": " + mDisplayInfo - + ", " + mTempMetrics; + + ", " + mTempMetrics + ", isValid=" + mIsValid; } } } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index e38f245be92b1..593e8c4700f07 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -138,6 +138,10 @@ public final class DisplayInfo implements Parcelable { public DisplayInfo() { } + public DisplayInfo(DisplayInfo other) { + copyFrom(other); + } + private DisplayInfo(Parcel source) { readFromParcel(source); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b1f5e9e58d362..9338a51351243 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -40,6 +40,7 @@ import android.graphics.Shader; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -7347,7 +7348,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, outRect.bottom -= insets.bottom; return; } - Display d = DisplayManager.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); + // The view is not attached to a display so we don't have a context. + // Make a best guess about the display size. + Display d = DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); d.getRectSize(outRect); } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 123d9e76071b1..51503fd718616 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -179,7 +179,8 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS, to = "TYPE_BOOT_PROGRESS"), @ViewDebug.IntToString(from = TYPE_HIDDEN_NAV_CONSUMER, to = "TYPE_HIDDEN_NAV_CONSUMER"), @ViewDebug.IntToString(from = TYPE_DREAM, to = "TYPE_DREAM"), - @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL, to = "TYPE_NAVIGATION_BAR_PANEL") + @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL, to = "TYPE_NAVIGATION_BAR_PANEL"), + @ViewDebug.IntToString(from = TYPE_DISPLAY_OVERLAY, to = "TYPE_DISPLAY_OVERLAY"), }) public int type; @@ -434,6 +435,12 @@ public interface WindowManager extends ViewManager { */ public static final int TYPE_UNIVERSE_BACKGROUND = FIRST_SYSTEM_WINDOW+25; + /** + * Window type: Display overlay window. Used to simulate secondary display devices. + * @hide + */ + public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26; + /** * End of types of system windows. */ diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index bf061df197913..aa9179f6309b1 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -52,8 +52,9 @@ public final class WindowManagerImpl implements WindowManager { private final Window mParentWindow; public WindowManagerImpl(Context context, int displayId) { + DisplayManager dm = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); mContext = context; - mDisplay = DisplayManager.getInstance().getDisplay(displayId, mContext); + mDisplay = dm.getDisplay(displayId); mParentWindow = null; } diff --git a/core/res/res/layout/overlay_display_window.xml b/core/res/res/layout/overlay_display_window.xml new file mode 100644 index 0000000000000..25c792a417e86 --- /dev/null +++ b/core/res/res/layout/overlay_display_window.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index d7619801a3285..68aa410284c92 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -120,6 +120,8 @@ + + @@ -479,7 +481,9 @@ - + + + @@ -1093,6 +1097,7 @@ + diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index e77dde7a9e12a..985dfc4768113 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3651,6 +3651,12 @@ - Built-in Screen + Built-in Screen + + + Overlay #%1$d + + + Overlay #%1$d: %2$dx%3$d, %4$d dpi diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 209ad38b24f52..65e85608c391a 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -108,6 +108,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVE import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_DRAG; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER; @@ -218,14 +219,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int NAVIGATION_BAR_PANEL_LAYER = 20; // system-level error dialogs static final int SYSTEM_ERROR_LAYER = 21; + // used to simulate secondary display devices + static final int DISPLAY_OVERLAY_LAYER = 22; // the drag layer: input for drag-and-drop is associated with this window, // which sits above all other focusable windows - static final int DRAG_LAYER = 22; - static final int SECURE_SYSTEM_OVERLAY_LAYER = 23; - static final int BOOT_PROGRESS_LAYER = 24; + static final int DRAG_LAYER = 23; + static final int SECURE_SYSTEM_OVERLAY_LAYER = 24; + static final int BOOT_PROGRESS_LAYER = 25; // the (mouse) pointer layer - static final int POINTER_LAYER = 25; - static final int HIDDEN_NAV_CONSUMER_LAYER = 26; + static final int POINTER_LAYER = 26; + static final int HIDDEN_NAV_CONSUMER_LAYER = 27; static final int APPLICATION_MEDIA_SUBLAYER = -2; static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1; @@ -1327,6 +1330,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return SCREENSAVER_LAYER; case TYPE_UNIVERSE_BACKGROUND: return UNIVERSE_BACKGROUND_LAYER; + case TYPE_DISPLAY_OVERLAY: + return DISPLAY_OVERLAY_LAYER; } Log.e(TAG, "Unknown window type: " + type); return APPLICATION_LAYER; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7097891fd1f0b..48b12155e0cf0 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 f9fa7a871f211..d19fe017d6d32 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. *

* For now, all display adapters are registered in the system server but * in principle it could be done from other processes. + *

+ * Display devices are not thread-safe and must only be accessed + * on the display manager service's handler thread. *

*/ -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 abstract void register(Listener listener); + 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. + */ + 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 4a6dd66e61340..c83ce96743248 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. + *

+ * Display devices are not thread-safe and must only be accessed + * on the display manager service's handler thread. + *

*/ 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 9c0f9644b1edb..c7b8c244e987e 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 1463780a32892..cf835ef209b45 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. *

- * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. *

*/ 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 ArrayList mDisplayAdapters = new ArrayList(); - private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo(); - private DisplayDevice mDefaultDisplayDevice; + private final DisplayManagerHandler mHandler; + private final DisplayAdapterListener mDisplayAdapterListener = new DisplayAdapterListener(); + private final SparseArray mCallbacks = + new SparseArray(); - public DisplayManagerService(Context context) { + // List of all currently registered display adapters. + private final ArrayList mDisplayAdapters = new ArrayList(); + + // List of all currently connected display devices. + private final ArrayList mDisplayDevices = new ArrayList(); + + // List of all logical displays, indexed by logical display id. + private final SparseArray mLogicalDisplays = new SparseArray(); + 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 mTempCallbacks = new ArrayList(); + + 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(); + // 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."); } - IBinder displayToken = mDefaultDisplayDevice.getDisplayToken(); - if (displayToken != null) { - Surface.openTransaction(); - try { - Surface.setDisplayOrientation(displayToken, orientation); - } finally { - Surface.closeTransaction(); + 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); } } } } - /** - * Save away new DisplayInfo data. - * @param displayId The logical display id. - * @param info The new data to be stored. - */ - public void setDisplayInfo(int displayId, DisplayInfo info) { + // Runs on handler. + private void handleDisplayDeviceRemoved(DisplayDevice device) { synchronized (mLock) { - if (displayId != Display.DEFAULT_DISPLAY) { - throw new UnsupportedOperationException(); + 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); + } } - mDefaultDisplayInfo.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) { - if (displayId != Display.DEFAULT_DISPLAY) { - return false; - } - outInfo.copyFrom(mDefaultDisplayInfo); - return true; + // 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); } - } - 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); + final int count; + synchronized (mLock) { + count = mCallbacks.size(); + mTempCallbacks.clear(); + for (int i = 0; i < count; i++) { + mTempCallbacks.add(mCallbacks.valueAt(i)); } + } - @Override - public void onDisplayDeviceRemoved(DisplayDevice device) { - } - }); - } - - 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; + 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 f984c5d90e372..f5c78b9f49308 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. + *

+ * Display adapters are not thread-safe and must only be accessed + * on the display manager service's handler thread. + *

*/ 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 fa778786c06b7..73544fc1fa9f5 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. + *

+ * Display adapters are not thread-safe and must only be accessed + * on the display manager service's handler thread. + *

*/ 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; + private final boolean mIsDefault; - public LocalDisplayDevice(IBinder token) { - mDisplayToken = token; - } - - @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 0000000000000..476d21ab08e10 --- /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. + *

+ * Display adapters are not thread-safe and must only be accessed + * on the display manager service's handler thread. + *

+ */ +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 mOverlays = new ArrayList(); + 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 cd211da099734..6b6d8994eec6e 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 c1047a98d0333..0d9db900c393f 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; - } + }); + return holder[0]; } - static class WMThread extends Thread { - WindowManagerService mService; + private void initPolicy(Handler uiHandler) { + uiHandler.runWithScissors(new Runnable() { + @Override + public void run() { + WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); - 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(); + mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); + mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer() + * TYPE_LAYER_MULTIPLIER + + TYPE_LAYER_OFFSET; } - - // 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(); - } - } - - static class PolicyThread extends Thread { - private final WindowManagerPolicy mPolicy; - private final WindowManagerService mService; - private final Context mContext; - boolean mRunning = false; - - 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"); - } - - 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;