Support HDMI hotplug.

Bug: 7206678
Change-Id: Ia5212b16658a5f5a2ccf8528eca7bebd45ca857a
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 @@
         }
 
         @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 @@
      *
      * @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 @@
 
     // 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 @@
     jclass clazz;
 
     jmethodID dispatchVsync;
+    jmethodID dispatchHotplug;
 } gDisplayEventReceiverClassInfo;
 
 
@@ -61,7 +62,9 @@
     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 @@
 
         // 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 @@
 
     // 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 @@
     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 @@
 
     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 @@
                 return;
             }
 
+            Slog.i(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
+
             mDisplayDevices.add(device);
             addLogicalDisplayLocked(device);
             scheduleTraversalLocked();
@@ -550,6 +552,8 @@
                 return;
             }
 
+            Slog.i(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
+
             device.applyPendingDisplayDeviceInfoChangesLocked();
             if (updateLogicalDisplaysLocked()) {
                 scheduleTraversalLocked();
@@ -565,6 +569,8 @@
                 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 @@
 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 @@
 
     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 @@
             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