libhwc2.1: add error handling for display hints

Add error handling in case the failure occurs while sending
the hints or checking the support. Also re-connect to PowerHAL
service if it belongs to binder transaction failure.

Bug: 189389118
Test: 1. Kill the PowerHAL service and it can re-connect
         successfully
      2. Force the binder failure and it can retry without
         blocking hwc thread
      3. Force the non-binder failure and it can retry again
         in next loop
Change-Id: I1cc29cdb37c95ffedde8aee71e684e856b1fae4e
diff --git a/libhwc2.1/libdevice/ExynosDisplay.cpp b/libhwc2.1/libdevice/ExynosDisplay.cpp
index 8f87c1d..5c2163b 100644
--- a/libhwc2.1/libdevice/ExynosDisplay.cpp
+++ b/libhwc2.1/libdevice/ExynosDisplay.cpp
@@ -58,6 +58,7 @@
       : Worker("DisplayHints", HAL_PRIORITY_URGENT_DISPLAY),
         mNeedUpdateRefreshRateHint(false),
         mPrevRefreshRate(0),
+        mPendingPrevRefreshRate(0),
         mIdleHintIsEnabled(false),
         mIdleHintDeadlineTime(0),
         mIdleHintSupportIsChecked(false),
@@ -65,13 +66,12 @@
         mPowerModeState(HWC2_POWER_MODE_OFF),
         mVsyncPeriod(16666666),
         mPowerHalExtAidl(nullptr) {
-    connectPowerHalExt();
     InitWorker();
 }
 
-void ExynosDisplay::PowerHalHintWorker::connectPowerHalExt() {
+int32_t ExynosDisplay::PowerHalHintWorker::connectPowerHalExt() {
     if (mPowerHalExtAidl) {
-        return;
+        return NO_ERROR;
     }
 
     const std::string kInstance = std::string(IPower::descriptor) + "/default";
@@ -81,20 +81,31 @@
     mPowerHalExtAidl = IPowerExt::fromBinder(pwExtBinder);
     if (!mPowerHalExtAidl) {
         ALOGE("failed to connect power HAL extension");
-        return;
+        return -EINVAL;
     }
 
     ALOGI("connect power HAL extension successfully");
+    return NO_ERROR;
 }
 
 int32_t ExynosDisplay::PowerHalHintWorker::checkPowerHalExtHintSupport(const std::string &mode) {
-    if (mode.empty() || !mPowerHalExtAidl) {
+    if (mode.empty() || connectPowerHalExt() != NO_ERROR) {
         return -EINVAL;
     }
 
     bool isSupported = false;
-    if (!mPowerHalExtAidl->isModeSupported(mode.c_str(), &isSupported).isOk()) {
+    auto ret = mPowerHalExtAidl->isModeSupported(mode.c_str(), &isSupported);
+    if (!ret.isOk()) {
         ALOGE("failed to check power HAL extension hint: mode=%s", mode.c_str());
+        if (ret.getExceptionCode() == EX_TRANSACTION_FAILED) {
+            /*
+             * PowerHAL service may crash due to some reasons, this could end up
+             * binder transaction failure. Set nullptr here to trigger re-connection.
+             */
+            ALOGE("binder transaction failed for power HAL extension hint");
+            mPowerHalExtAidl = nullptr;
+            return -ENOTCONN;
+        }
         return -EINVAL;
     }
 
@@ -109,16 +120,25 @@
 
 int32_t ExynosDisplay::PowerHalHintWorker::sendPowerHalExtHint(const std::string &mode,
                                                                bool enabled) {
-    if (mode.empty() || !mPowerHalExtAidl) {
+    if (mode.empty() || connectPowerHalExt() != NO_ERROR) {
         return -EINVAL;
     }
 
-    if (!mPowerHalExtAidl->setMode(mode.c_str(), enabled).isOk()) {
+    auto ret = mPowerHalExtAidl->setMode(mode.c_str(), enabled);
+    if (!ret.isOk()) {
         ALOGE("failed to send power HAL extension hint: mode=%s, enabled=%d", mode.c_str(),
               enabled);
+        if (ret.getExceptionCode() == EX_TRANSACTION_FAILED) {
+            /*
+             * PowerHAL service may crash due to some reasons, this could end up
+             * binder transaction failure. Set nullptr here to trigger re-connection.
+             */
+            ALOGE("binder transaction failed for power HAL extension hint");
+            mPowerHalExtAidl = nullptr;
+            return -ENOTCONN;
+        }
         return -EINVAL;
     }
-
     return NO_ERROR;
 }
 
@@ -141,62 +161,95 @@
             ret = -EOPNOTSUPP;
         }
     }
-
     return ret;
 }
 
-void ExynosDisplay::PowerHalHintWorker::updateRefreshRateHintInternal(hwc2_power_mode_t powerMode,
-                                                                      uint32_t vsyncPeriod) {
+int32_t ExynosDisplay::PowerHalHintWorker::sendRefreshRateHint(int refreshRate, bool enabled) {
+    std::string hintStr = "REFRESH_" + std::to_string(refreshRate) + "FPS";
+    int32_t ret = sendPowerHalExtHint(hintStr, enabled);
+    if (ret == -ENOTCONN) {
+        /* Reset the hints when binder failure occurs */
+        mPrevRefreshRate = 0;
+        mPendingPrevRefreshRate = 0;
+    }
+    return ret;
+}
+
+int32_t ExynosDisplay::PowerHalHintWorker::updateRefreshRateHintInternal(
+        hwc2_power_mode_t powerMode, uint32_t vsyncPeriod) {
+    int32_t ret = NO_ERROR;
+    /* We should disable pending hint before other operations */
+    if (mPendingPrevRefreshRate) {
+        ret = sendRefreshRateHint(mPendingPrevRefreshRate, false);
+        if (ret == NO_ERROR) {
+            mPendingPrevRefreshRate = 0;
+        } else {
+            return ret;
+        }
+    }
+
     if (powerMode != HWC2_POWER_MODE_ON) {
         if (mPrevRefreshRate) {
-            std::string prevRefreshRateHintStr =
-                    "REFRESH_" + std::to_string(mPrevRefreshRate) + "FPS";
-            if (sendPowerHalExtHint(prevRefreshRateHintStr, false) != NO_ERROR) {
-                ALOGE("failed to disable the refresh rate hint: %s",
-                      prevRefreshRateHintStr.c_str());
-            } else {
+            ret = sendRefreshRateHint(mPrevRefreshRate, false);
+            if (ret == NO_ERROR) {
                 mPrevRefreshRate = 0;
             }
         }
-        return;
+        return ret;
     }
 
     /* TODO: add refresh rate buckets, tracked in b/181100731 */
     int refreshRate = round(nsecsPerSec / vsyncPeriod * 0.1f) * 10;
     if (mPrevRefreshRate == refreshRate) {
-        return;
+        return NO_ERROR;
     }
 
-    if (checkRefreshRateHintSupport(refreshRate) != NO_ERROR) {
-        return;
+    ret = checkRefreshRateHintSupport(refreshRate);
+    if (ret != NO_ERROR) {
+        return ret;
     }
 
-    std::string refreshRateHintStr = "REFRESH_" + std::to_string(refreshRate) + "FPS";
-    if (sendPowerHalExtHint(refreshRateHintStr, true) != NO_ERROR) {
-        ALOGE("failed to enable refresh rate hint: %s", refreshRateHintStr.c_str());
-        return;
+    /*
+     * According to PowerHAL design, while switching to next refresh rate, we
+     * have to enable the next hint first, then disable the previous one so
+     * that the next hint can take effect.
+     */
+    ret = sendRefreshRateHint(refreshRate, true);
+    if (ret != NO_ERROR) {
+        return ret;
     }
 
     if (mPrevRefreshRate) {
-        std::string prevRefreshRateHintStr = "REFRESH_" + std::to_string(mPrevRefreshRate) + "FPS";
-        if (sendPowerHalExtHint(prevRefreshRateHintStr, false) != NO_ERROR) {
-            ALOGE("failed to disable refresh rate hint: REFRESH_%dFPS", mPrevRefreshRate);
-            return;
+        ret = sendRefreshRateHint(mPrevRefreshRate, false);
+        if (ret != NO_ERROR) {
+            if (ret != -ENOTCONN) {
+                /*
+                 * We may fail to disable the previous hint and end up multiple
+                 * hints enabled. Save the failed hint as pending hint here, we
+                 * will try to disable it first while entering this function.
+                 */
+                mPendingPrevRefreshRate = mPrevRefreshRate;
+                mPrevRefreshRate = refreshRate;
+            }
+            return ret;
         }
     }
 
     mPrevRefreshRate = refreshRate;
+    return ret;
 }
 
-void ExynosDisplay::PowerHalHintWorker::checkIdleHintSupport(void) {
+int32_t ExynosDisplay::PowerHalHintWorker::checkIdleHintSupport(void) {
+    int32_t ret = NO_ERROR;
     Lock();
     if (mIdleHintSupportIsChecked) {
+        ret = mIdleHintIsSupported ? NO_ERROR : -EOPNOTSUPP;
         Unlock();
-        return;
+        return ret;
     }
     Unlock();
 
-    int32_t ret = checkPowerHalExtHintSupport("DISPLAY_IDLE");
+    ret = checkPowerHalExtHintSupport("DISPLAY_IDLE");
     Lock();
     if (ret == NO_ERROR) {
         mIdleHintIsSupported = true;
@@ -209,19 +262,25 @@
         ALOGW("failed to check the support of display idle hint, ret %d", ret);
     }
     Unlock();
+    return ret;
 }
 
-void ExynosDisplay::PowerHalHintWorker::updateIdleHint(uint64_t deadlineTime) {
+int32_t ExynosDisplay::PowerHalHintWorker::updateIdleHint(uint64_t deadlineTime) {
+    int32_t ret = checkIdleHintSupport();
+    if (ret != NO_ERROR) {
+        return ret;
+    }
+
     bool enableIdleHint = (deadlineTime < systemTime(SYSTEM_TIME_MONOTONIC));
     ATRACE_INT("HWCIdleHintTimer:", enableIdleHint);
 
     if (mIdleHintIsEnabled != enableIdleHint) {
-        if (sendPowerHalExtHint("DISPLAY_IDLE", enableIdleHint) != NO_ERROR) {
-            ALOGE("failed to send DISPLAY_IDLE hint: %d", enableIdleHint);
-            return;
+        ret = sendPowerHalExtHint("DISPLAY_IDLE", enableIdleHint);
+        if (ret == NO_ERROR) {
+            mIdleHintIsEnabled = enableIdleHint;
         }
-        mIdleHintIsEnabled = enableIdleHint;
     }
+    return ret;
 }
 
 void ExynosDisplay::PowerHalHintWorker::signalRefreshRate(hwc2_power_mode_t powerMode,
@@ -251,7 +310,6 @@
 }
 
 void ExynosDisplay::PowerHalHintWorker::Routine() {
-    checkIdleHintSupport();
     Lock();
     int ret = 0;
     if (!mNeedUpdateRefreshRateHint) {
@@ -276,15 +334,28 @@
     hwc2_power_mode_t powerMode = mPowerModeState;
     uint32_t vsyncPeriod = mVsyncPeriod;
 
+    /*
+     * Clear the flags here instead of clearing them after calling the hint
+     * update functions. The flags may be set by signals after Unlock() and
+     * before the hint update functions are done. Thus we may miss the newest
+     * hints if we clear the flags after the hint update functions work without
+     * errors.
+     */
     mNeedUpdateRefreshRateHint = false;
     Unlock();
 
-    if (mIdleHintIsSupported) {
-        updateIdleHint(deadlineTime);
-    }
+    updateIdleHint(deadlineTime);
 
     if (needUpdateRefreshRateHint) {
-        updateRefreshRateHintInternal(powerMode, vsyncPeriod);
+        int32_t rc = updateRefreshRateHintInternal(powerMode, vsyncPeriod);
+        if (rc != NO_ERROR && rc != -EOPNOTSUPP) {
+            Lock();
+            if (mPowerModeState == HWC2_POWER_MODE_ON) {
+                /* Set the flag to trigger update again for next loop */
+                mNeedUpdateRefreshRateHint = true;
+            }
+            Unlock();
+        }
     }
 }
 
diff --git a/libhwc2.1/libdevice/ExynosDisplay.h b/libhwc2.1/libdevice/ExynosDisplay.h
index 6effb91..98532ab 100644
--- a/libhwc2.1/libdevice/ExynosDisplay.h
+++ b/libhwc2.1/libdevice/ExynosDisplay.h
@@ -1201,21 +1201,26 @@
             void Routine() override;
 
         private:
-            void connectPowerHalExt();
+            int32_t connectPowerHalExt();
             int32_t checkPowerHalExtHintSupport(const std::string& mode);
             int32_t sendPowerHalExtHint(const std::string& mode, bool enabled);
 
             int32_t checkRefreshRateHintSupport(int refreshRate);
-            void updateRefreshRateHintInternal(hwc2_power_mode_t powerMode, uint32_t vsyncPeriod);
+            int32_t updateRefreshRateHintInternal(hwc2_power_mode_t powerMode,
+                                                  uint32_t vsyncPeriod);
+            int32_t sendRefreshRateHint(int refreshRate, bool enabled);
 
-            void checkIdleHintSupport();
-            void updateIdleHint(uint64_t deadlineTime);
+            int32_t checkIdleHintSupport();
+            int32_t updateIdleHint(uint64_t deadlineTime);
 
             bool mNeedUpdateRefreshRateHint;
 
             // previous refresh rate
             int mPrevRefreshRate;
 
+            // the refresh rate whose hint failed to be disabled
+            int mPendingPrevRefreshRate;
+
             // support list of refresh rate hints
             std::map<int, bool> mRefreshRateHintSupportMap;
 
diff --git a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp
index 87710fd..c69388c 100644
--- a/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp
+++ b/libhwc2.1/libmaindisplay/ExynosPrimaryDisplay.cpp
@@ -287,6 +287,8 @@
 
     mPowerModeState = mode;
 
+    ExynosDisplay::updateRefreshRateHint();
+
     return HWC2_ERROR_NONE;
 }