summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2012-09-20 18:30:13 -0700
committerJeff Brown <jeffbrown@google.com>2012-09-21 15:34:32 -0700
commite87bf030766198bf5e1fe846167dba766e27fb3f (patch)
treeb0f379481622310b6afd5e8002c16930e6ed91b6
parentf98db0de2248ad286b207eed6a826373cc60b786 (diff)
downloadframeworks_base-e87bf030766198bf5e1fe846167dba766e27fb3f.zip
frameworks_base-e87bf030766198bf5e1fe846167dba766e27fb3f.tar.gz
frameworks_base-e87bf030766198bf5e1fe846167dba766e27fb3f.tar.bz2
Support HDMI hotplug.
Bug: 7206678 Change-Id: Ia5212b16658a5f5a2ccf8528eca7bebd45ca857a
-rw-r--r--core/java/android/view/Choreographer.java20
-rw-r--r--core/java/android/view/DisplayEventReceiver.java26
-rw-r--r--core/jni/android_view_DisplayEventReceiver.cpp65
-rw-r--r--services/java/com/android/server/display/DisplayManagerService.java6
-rw-r--r--services/java/com/android/server/display/LocalDisplayAdapter.java19
5 files changed, 112 insertions, 24 deletions
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 6848606..b661748 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -16,7 +16,6 @@
package android.view;
-import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.Looper;
@@ -685,7 +684,24 @@ public final class Choreographer {
}
@Override
- public void onVsync(long timestampNanos, int frame) {
+ public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
+ // Ignore vsync from secondary display.
+ // This can be problematic because the call to scheduleVsync() is a one-shot.
+ // We need to ensure that we will still receive the vsync from the primary
+ // display which is the one we really care about. Ideally we should schedule
+ // vsync for a particular display.
+ // At this time Surface Flinger won't send us vsyncs for secondary displays
+ // but that could change in the future so let's log a message to help us remember
+ // that we need to fix this.
+ if (builtInDisplayId != Surface.BUILT_IN_DISPLAY_ID_MAIN) {
+ Log.d(TAG, "Received vsync from secondary display, but we don't support "
+ + "this case yet. Choreographer needs a way to explicitly request "
+ + "vsync for a specific display to ensure it doesn't lose track "
+ + "of its scheduled vsync.");
+ scheduleVsync();
+ return;
+ }
+
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 0b138c2..a919ffc 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -101,9 +101,23 @@ public abstract class DisplayEventReceiver {
*
* @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()}
* timebase.
+ * @param builtInDisplayId The surface flinger built-in display id such as
+ * {@link Surface#BUILT_IN_DISPLAY_ID_MAIN}.
* @param frame The frame number. Increases by one for each vertical sync interval.
*/
- public void onVsync(long timestampNanos, int frame) {
+ public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
+ }
+
+ /**
+ * Called when a display hotplug event is received.
+ *
+ * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
+ * timebase.
+ * @param builtInDisplayId The surface flinger built-in display id such as
+ * {@link Surface#BUILT_IN_DISPLAY_ID_HDMI}.
+ * @param connected True if the display is connected, false if it disconnected.
+ */
+ public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
}
/**
@@ -121,7 +135,13 @@ public abstract class DisplayEventReceiver {
// Called from native code.
@SuppressWarnings("unused")
- private void dispatchVsync(long timestampNanos, int frame) {
- onVsync(timestampNanos, frame);
+ private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
+ onVsync(timestampNanos, builtInDisplayId, frame);
+ }
+
+ // Called from native code.
+ @SuppressWarnings("unused")
+ private void dispatchHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
+ onHotplug(timestampNanos, builtInDisplayId, connected);
}
}
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 89058a7..3d9d005 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -39,6 +39,7 @@ static struct {
jclass clazz;
jmethodID dispatchVsync;
+ jmethodID dispatchHotplug;
} gDisplayEventReceiverClassInfo;
@@ -61,7 +62,9 @@ private:
bool mWaitingForVsync;
virtual int handleEvent(int receiveFd, int events, void* data);
- bool readLastVsyncMessage(nsecs_t* outTimestamp, uint32_t* outCount);
+ bool readLastVsyncMessage(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount);
+ void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count);
+ void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);
};
@@ -106,8 +109,9 @@ status_t NativeDisplayEventReceiver::scheduleVsync() {
// Drain all pending events.
nsecs_t vsyncTimestamp;
+ int32_t vsyncDisplayId;
uint32_t vsyncCount;
- readLastVsyncMessage(&vsyncTimestamp, &vsyncCount);
+ readLastVsyncMessage(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount);
status_t status = mReceiver.requestNextVsync();
if (status) {
@@ -135,39 +139,39 @@ int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* dat
// Drain all pending events, keep the last vsync.
nsecs_t vsyncTimestamp;
+ int32_t vsyncDisplayId;
uint32_t vsyncCount;
- if (!readLastVsyncMessage(&vsyncTimestamp, &vsyncCount)) {
+ if (!readLastVsyncMessage(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
ALOGV("receiver %p ~ Woke up but there was no vsync pulse!", this);
return 1; // keep the callback, did not obtain a vsync pulse
}
- ALOGV("receiver %p ~ Vsync pulse: timestamp=%lld, count=%d",
- this, vsyncTimestamp, vsyncCount);
+ ALOGV("receiver %p ~ Vsync pulse: timestamp=%lld, id=%d, count=%d",
+ this, vsyncTimestamp, vsyncDisplayId, vsyncCount);
mWaitingForVsync = false;
- JNIEnv* env = AndroidRuntime::getJNIEnv();
-
- ALOGV("receiver %p ~ Invoking vsync handler.", this);
- env->CallVoidMethod(mReceiverObjGlobal,
- gDisplayEventReceiverClassInfo.dispatchVsync, vsyncTimestamp, vsyncCount);
- ALOGV("receiver %p ~ Returned from vsync handler.", this);
-
- mMessageQueue->raiseAndClearException(env, "dispatchVsync");
+ dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
return 1; // keep the callback
}
bool NativeDisplayEventReceiver::readLastVsyncMessage(
- nsecs_t* outTimestamp, uint32_t* outCount) {
+ nsecs_t* outTimestamp, int32_t* outId, uint32_t* outCount) {
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
ssize_t n;
while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
ALOGV("receiver %p ~ Read %d events.", this, int(n));
while (n-- > 0) {
- if (buf[n].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
- *outTimestamp = buf[n].header.timestamp;
- *outCount = buf[n].vsync.count;
+ const DisplayEventReceiver::Event& ev = buf[n];
+ if (ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ *outTimestamp = ev.header.timestamp;
+ *outId = ev.header.id;
+ *outCount = ev.vsync.count;
return true; // stop at last vsync in the buffer
}
+
+ if (ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
+ dispatchHotplug(ev.header.timestamp, ev.header.id, ev.hotplug.connected);
+ }
}
}
if (n < 0) {
@@ -176,6 +180,28 @@ bool NativeDisplayEventReceiver::readLastVsyncMessage(
return false;
}
+void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ ALOGV("receiver %p ~ Invoking vsync handler.", this);
+ env->CallVoidMethod(mReceiverObjGlobal,
+ gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
+ ALOGV("receiver %p ~ Returned from vsync handler.", this);
+
+ mMessageQueue->raiseAndClearException(env, "dispatchVsync");
+}
+
+void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ ALOGV("receiver %p ~ Invoking hotplug handler.", this);
+ env->CallVoidMethod(mReceiverObjGlobal,
+ gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, id, connected);
+ ALOGV("receiver %p ~ Returned from hotplug handler.", this);
+
+ mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
+}
+
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
jobject messageQueueObj) {
@@ -248,7 +274,10 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) {
GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchVsync,
gDisplayEventReceiverClassInfo.clazz,
- "dispatchVsync", "(JI)V");
+ "dispatchVsync", "(JII)V");
+ GET_METHOD_ID(gDisplayEventReceiverClassInfo.dispatchHotplug,
+ gDisplayEventReceiverClassInfo.clazz,
+ "dispatchHotplug", "(JIZ)V");
return 0;
}
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index b109f2a..85f3b56 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -536,6 +536,8 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
return;
}
+ Slog.i(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
+
mDisplayDevices.add(device);
addLogicalDisplayLocked(device);
scheduleTraversalLocked();
@@ -550,6 +552,8 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
return;
}
+ Slog.i(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
+
device.applyPendingDisplayDeviceInfoChangesLocked();
if (updateLogicalDisplaysLocked()) {
scheduleTraversalLocked();
@@ -565,6 +569,8 @@ public final class DisplayManagerService extends IDisplayManager.Stub {
return;
}
+ Slog.i(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
+
mRemovedDisplayDevices.add(device);
updateLogicalDisplaysLocked();
scheduleTraversalLocked();
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index eab4c9a..9c51463 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -19,7 +19,9 @@ package com.android.server.display;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.util.SparseArray;
+import android.view.DisplayEventReceiver;
import android.view.Surface;
import android.view.Surface.PhysicalDisplayInfo;
@@ -41,12 +43,14 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private final SparseArray<LocalDisplayDevice> mDevices =
new SparseArray<LocalDisplayDevice>();
+ private final HotplugDisplayEventReceiver mHotplugReceiver;
private final PhysicalDisplayInfo mTempPhys = new PhysicalDisplayInfo();
public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener) {
super(syncRoot, context, handler, listener, TAG);
+ mHotplugReceiver = new HotplugDisplayEventReceiver(handler.getLooper());
}
@Override
@@ -148,4 +152,17 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("mPhys=" + mPhys);
}
}
-}
+
+ private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
+ public HotplugDisplayEventReceiver(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
+ synchronized (getSyncRoot()) {
+ scanDisplaysLocked();
+ }
+ }
+ }
+} \ No newline at end of file