Merge "sdm: hwc2: Add static uevent thread."
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index 7f39d7f..43f71ed 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -35,6 +35,8 @@
 #include <algorithm>
 #include <string>
 #include <bitset>
+#include <thread>
+#include <memory>
 
 #include "hwc_buffer_allocator.h"
 #include "hwc_buffer_sync_handler.h"
@@ -66,8 +68,55 @@
 };
 
 namespace sdm {
+
+static HWCUEvent g_hwc_uevent_;
 Locker HWCSession::locker_;
 
+void * HWCUEvent::UEventThread(HWCUEvent *hwc_event) {
+  const char *uevent_thread_name = "HWC_UeventThread";
+
+  prctl(PR_SET_NAME, uevent_thread_name, 0, 0, 0);
+  setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+  if (!uevent_init()) {
+    DLOGE("Failed to init uevent");
+    pthread_exit(0);
+    return NULL;
+  }
+
+  while (1) {
+    char uevent_data[PAGE_SIZE] = {};
+
+    // keep last 2 zeroes to ensure double 0 termination
+    int length = uevent_next_event(uevent_data, INT32(sizeof(uevent_data)) - 2);
+
+    // scope of lock to this block only, so that caller is free to set event handler to nullptr;
+    {
+      std::lock_guard<std::mutex> guard(hwc_event->mutex_);
+      if (hwc_event->event_handler_) {
+        hwc_event->event_handler_->UEvent(uevent_data, length);
+      } else {
+        DLOGW("UEvent dropped. No uevent listener.");
+      }
+    }
+  }
+  pthread_exit(0);
+
+  return NULL;
+}
+
+HWCUEvent::HWCUEvent() {
+  std::thread thread(HWCUEvent::UEventThread, this);
+  thread.detach();
+}
+
+void HWCUEvent::Register(HWCUEventHandler *event_handler) {
+  DLOGI("Set uevent listener = %p", event_handler);
+
+  std::lock_guard<std::mutex> obj(mutex_);
+  event_handler_ = event_handler;
+}
+
 HWCSession::HWCSession(const hw_module_t *module) {
   hwc2_device_t::common.tag = HARDWARE_DEVICE_TAG;
   hwc2_device_t::common.version = HWC_DEVICE_API_VERSION_2_0;
@@ -146,8 +195,7 @@
     }
   }
 
-  std::thread uevent_thread(HWCUeventThread, this);
-  uevent_thread_.swap(uevent_thread);
+  g_hwc_uevent_.Register(this);
 
   return 0;
 }
@@ -167,10 +215,7 @@
     color_mgr_->DestroyColorManager();
   }
 
-  DLOGD("Terminating uevent thread");
-  uevent_thread_exit_ = true;
-  // todo(user): add a local event to interrupt thread execution and join.
-  uevent_thread_.detach();
+  g_hwc_uevent_.Register(nullptr);
 
   DisplayError error = CoreInterface::DestroyCore();
   if (error != kErrorNone) {
@@ -1238,52 +1283,26 @@
   return (ret ? -EINVAL : 0);
 }
 
-void *HWCSession::HWCUeventThread(void *context) {
-  if (context) {
-    return reinterpret_cast<HWCSession *>(context)->HWCUeventThreadHandler();
-  }
-
-  return NULL;
-}
-
-void *HWCSession::HWCUeventThreadHandler() {
-  static char uevent_data[PAGE_SIZE];
-  int length = 0;
-  prctl(PR_SET_NAME, uevent_thread_name_, 0, 0, 0);
-  setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
-  if (!uevent_init()) {
-    DLOGE("Failed to init uevent");
-    pthread_exit(0);
-    return NULL;
-  }
-
-  while (!uevent_thread_exit_) {
-    // keep last 2 zeroes to ensure double 0 termination
-    length = uevent_next_event(uevent_data, INT32(sizeof(uevent_data)) - 2);
-
-    if (strcasestr(HWC_UEVENT_SWITCH_HDMI, uevent_data)) {
-      DLOGI("Uevent HDMI = %s", uevent_data);
-      int connected = GetEventValue(uevent_data, length, "SWITCH_STATE=");
-      if (connected >= 0) {
-        DLOGI("HDMI = %s", connected ? "connected" : "disconnected");
-        if (HotPlugHandler(connected) == -1) {
-          DLOGE("Failed handling Hotplug = %s", connected ? "connected" : "disconnected");
-        }
+void HWCSession::UEvent(const char *uevent_data, int length) {
+  if (strcasestr(uevent_data, HWC_UEVENT_SWITCH_HDMI)) {
+    DLOGI("Uevent HDMI = %s", uevent_data);
+    int connected = GetEventValue(uevent_data, length, "SWITCH_STATE=");
+    if (connected >= 0) {
+      DLOGI("HDMI = %s", connected ? "connected" : "disconnected");
+      if (HotPlugHandler(connected) == -1) {
+        DLOGE("Failed handling Hotplug = %s", connected ? "connected" : "disconnected");
       }
-    } else if (strcasestr(HWC_UEVENT_GRAPHICS_FB0, uevent_data)) {
-      DLOGI("Uevent FB0 = %s", uevent_data);
-      int panel_reset = GetEventValue(uevent_data, length, "PANEL_ALIVE=");
-      if (panel_reset == 0) {
-        callbacks_.Refresh(0);
-        reset_panel_ = true;
-      }
-    } else if (strcasestr(uevent_data, HWC_UEVENT_DRM_EXT_HOTPLUG)) {
-      HandleExtHPD(uevent_data, length);
     }
+  } else if (strcasestr(uevent_data, HWC_UEVENT_GRAPHICS_FB0)) {
+    DLOGI("Uevent FB0 = %s", uevent_data);
+    int panel_reset = GetEventValue(uevent_data, length, "PANEL_ALIVE=");
+    if (panel_reset == 0) {
+      callbacks_.Refresh(0);
+      reset_panel_ = true;
+    }
+  } else if (strcasestr(uevent_data, HWC_UEVENT_DRM_EXT_HOTPLUG)) {
+    HandleExtHPD(uevent_data, length);
   }
-  pthread_exit(0);
-
-  return NULL;
 }
 
 void HWCSession::HandleExtHPD(const char *uevent_data, int length) {
diff --git a/sdm/libs/hwc2/hwc_session.h b/sdm/libs/hwc2/hwc_session.h
index cd9831d..825d605 100644
--- a/sdm/libs/hwc2/hwc_session.h
+++ b/sdm/libs/hwc2/hwc_session.h
@@ -23,7 +23,6 @@
 #include <vendor/display/config/1.0/IDisplayConfig.h>
 #include <core/core_interface.h>
 #include <utils/locker.h>
-#include <thread>
 
 #include "hwc_callbacks.h"
 #include "hwc_layers.h"
@@ -39,7 +38,29 @@
 using ::vendor::display::config::V1_0::IDisplayConfig;
 using ::android::hardware::Return;
 
-class HWCSession : hwc2_device_t, public IDisplayConfig, public qClient::BnQClient {
+// Create a singleton uevent listener thread valid for life of hardware composer process.
+// This thread blocks on uevents poll inside uevent library implementation. This poll exits
+// only when there is a valid uevent, it can not be interrupted otherwise. Tieing life cycle
+// of this thread with HWC session cause HWC deinitialization to wait infinitely for the
+// thread to exit.
+class HWCUEventHandler {
+ public:
+  virtual ~HWCUEventHandler() {}
+  virtual void UEvent(const char *uevent_data, int length) = 0;
+};
+
+class HWCUEvent {
+ public:
+  HWCUEvent();
+  static void *UEventThread(HWCUEvent *hwc_event);
+  void Register(HWCUEventHandler *event_handler);
+
+ private:
+  std::mutex mutex_;
+  HWCUEventHandler *event_handler_ = nullptr;
+};
+
+class HWCSession : hwc2_device_t, HWCUEventHandler, IDisplayConfig, public qClient::BnQClient {
  public:
   struct HWCModuleMethods : public hw_module_methods_t {
     HWCModuleMethods() { hw_module_methods_t::open = HWCSession::Open; }
@@ -120,9 +141,8 @@
                               int32_t *outCapabilities);
   static hwc2_function_pointer_t GetFunction(struct hwc2_device *device, int32_t descriptor);
 
-  // Uevent thread
-  static void *HWCUeventThread(void *context);
-  void *HWCUeventThreadHandler();
+  // Uevent handler
+  virtual void UEvent(const char *uevent_data, int length);
   int GetEventValue(const char *uevent_data, int length, const char *event_info);
   void HandleExtHPD(const char *uevent_data, int length);
   int HotPlugHandler(bool connected);
@@ -192,9 +212,6 @@
   CoreInterface *core_intf_ = nullptr;
   HWCDisplay *hwc_display_[HWC_NUM_DISPLAY_TYPES] = {nullptr};
   HWCCallbacks callbacks_;
-  std::thread uevent_thread_;
-  bool uevent_thread_exit_ = false;
-  const char *uevent_thread_name_ = "HWC_UeventThread";
   HWCBufferAllocator buffer_allocator_;
   HWCBufferSyncHandler buffer_sync_handler_;
   HWCColorManager *color_mgr_ = nullptr;