hwc/qdutils/qservice: Add dynamic idle timeout support

Add support for dynamically setting idle timeout values.
Move default idle timeout setting to IdleInvalidator.
Fix static var naming, handle errors from IdleInvalidator in hwc.
Property debug.hwc.idletime is removed with this change.

Example:
1) Set idle timeout to 100ms
adb shell service call display.qservice 16 i32 100
16 is the code for SET_IDLE_TIMEOUT, 100 is time in ms

2) Disable idle timeout
adb shell service call display.qservice 16 i32 0

Change-Id: I60e15a3ac869b4e9f4015b3be50a35c90d00d404
diff --git a/libhwcomposer/hwc_mdpcomp.cpp b/libhwcomposer/hwc_mdpcomp.cpp
index 82d1f05..937f156 100644
--- a/libhwcomposer/hwc_mdpcomp.cpp
+++ b/libhwcomposer/hwc_mdpcomp.cpp
@@ -36,7 +36,7 @@
 
 //==============MDPComp========================================================
 
-IdleInvalidator *MDPComp::idleInvalidator = NULL;
+IdleInvalidator *MDPComp::sIdleInvalidator = NULL;
 bool MDPComp::sIdleFallBack = false;
 bool MDPComp::sHandleTimeout = false;
 bool MDPComp::sDebugLogs = false;
@@ -110,7 +110,7 @@
         return false;
     }
 
-    char property[PROPERTY_VALUE_MAX];
+    char property[PROPERTY_VALUE_MAX] = {0};
 
     sEnabled = false;
     if((property_get("persist.hwc.mdpcomp.enable", property, NULL) > 0) &&
@@ -134,23 +134,10 @@
     }
 
     if(ctx->mMDP.panel != MIPI_CMD_PANEL) {
-        // Idle invalidation is not necessary on command mode panels
-        long idle_timeout = DEFAULT_IDLE_TIME;
-        if(property_get("debug.mdpcomp.idletime", property, NULL) > 0) {
-            if(atoi(property) != 0)
-                idle_timeout = atoi(property);
-        }
-
-        //create Idle Invalidator only when not disabled through property
-        if(idle_timeout != -1)
-            idleInvalidator = IdleInvalidator::getInstance();
-
-        if(idleInvalidator == NULL) {
-            ALOGE("%s: failed to instantiate idleInvalidator object",
-                  __FUNCTION__);
-        } else {
-            idleInvalidator->init(timeout_handler, ctx,
-                                  (unsigned int)idle_timeout);
+        sIdleInvalidator = IdleInvalidator::getInstance();
+        if(sIdleInvalidator->init(timeout_handler, ctx) < 0) {
+            delete sIdleInvalidator;
+            sIdleInvalidator = NULL;
         }
     }
 
@@ -206,6 +193,25 @@
     ctx->proc->invalidate(ctx->proc);
 }
 
+void MDPComp::setIdleTimeout(const uint32_t& timeout) {
+    enum { ONE_REFRESH_PERIOD_MS = 17, ONE_BILLION_MS = 1000000000 };
+
+    if(sIdleInvalidator) {
+        if(timeout <= ONE_REFRESH_PERIOD_MS) {
+            //If the specified timeout is < 1 draw cycle worth, "virtually"
+            //disable idle timeout. The ideal way for clients to disable
+            //timeout is to set it to 0
+            sIdleInvalidator->setIdleTimeout(ONE_BILLION_MS);
+            ALOGI("Disabled idle timeout");
+            return;
+        }
+        sIdleInvalidator->setIdleTimeout(timeout);
+        ALOGI("Idle timeout set to %u", timeout);
+    } else {
+        ALOGW("Cannot set idle timeout, IdleInvalidator not enabled");
+    }
+}
+
 void MDPComp::setMDPCompLayerFlags(hwc_context_t *ctx,
                                    hwc_display_contents_1_t* list) {
     LayerProp *layerProp = ctx->layerProp[mDpy];
@@ -2055,7 +2061,7 @@
     }
 
     // Set the Handle timeout to true for MDP or MIXED composition.
-    if(idleInvalidator && !sIdleFallBack && mCurrentFrame.mdpCount) {
+    if(sIdleInvalidator && !sIdleFallBack && mCurrentFrame.mdpCount) {
         sHandleTimeout = true;
     }
 
@@ -2310,7 +2316,7 @@
     }
 
     // Set the Handle timeout to true for MDP or MIXED composition.
-    if(idleInvalidator && !sIdleFallBack && mCurrentFrame.mdpCount) {
+    if(sIdleInvalidator && !sIdleFallBack && mCurrentFrame.mdpCount) {
         sHandleTimeout = true;
     }
 
diff --git a/libhwcomposer/hwc_mdpcomp.h b/libhwcomposer/hwc_mdpcomp.h
index e43c4f4..268e9aa 100644
--- a/libhwcomposer/hwc_mdpcomp.h
+++ b/libhwcomposer/hwc_mdpcomp.h
@@ -25,7 +25,6 @@
 #include <cutils/properties.h>
 #include <overlay.h>
 
-#define DEFAULT_IDLE_TIME 70
 #define MAX_PIPES_PER_MIXER 4
 
 namespace overlay {
@@ -57,6 +56,7 @@
     static void resetIdleFallBack() { sIdleFallBack = false; }
     static bool isIdleFallback() { return sIdleFallBack; }
     static void dynamicDebug(bool enable){ sDebugLogs = enable; }
+    static void setIdleTimeout(const uint32_t& timeout);
 
 protected:
     enum { MAX_SEC_LAYERS = 1 }; //TODO add property support
@@ -255,7 +255,7 @@
     static bool sHandleTimeout;
     static int sMaxPipesPerMixer;
     static bool sSrcSplitEnabled;
-    static IdleInvalidator *idleInvalidator;
+    static IdleInvalidator *sIdleInvalidator;
     struct FrameInfo mCurrentFrame;
     struct LayerCache mCachedFrame;
     //Enable 4kx2k yuv layer split
diff --git a/libhwcomposer/hwc_qclient.cpp b/libhwcomposer/hwc_qclient.cpp
index 8490058..6bde3d2 100644
--- a/libhwcomposer/hwc_qclient.cpp
+++ b/libhwcomposer/hwc_qclient.cpp
@@ -245,6 +245,13 @@
     }
 }
 
+static void setIdleTimeout(hwc_context_t* ctx, const Parcel* inParcel) {
+    uint32_t timeout = (uint32_t)inParcel->readInt32();
+    ALOGD("%s :%u ms", __FUNCTION__, timeout);
+    Locker::Autolock _sl(ctx->mDrawLock);
+    MDPComp::setIdleTimeout(timeout);
+}
+
 status_t QClient::notifyCallback(uint32_t command, const Parcel* inParcel,
         Parcel* outParcel) {
     status_t ret = NO_ERROR;
@@ -290,6 +297,9 @@
         case IQService::DYNAMIC_DEBUG:
             toggleDynamicDebug(mHwcContext, inParcel);
             break;
+        case IQService::SET_IDLE_TIMEOUT:
+            setIdleTimeout(mHwcContext, inParcel);
+            break;
         default:
             ret = NO_ERROR;
     }
diff --git a/libqdutils/idle_invalidator.cpp b/libqdutils/idle_invalidator.cpp
index 86191e9..5850ce5 100644
--- a/libqdutils/idle_invalidator.cpp
+++ b/libqdutils/idle_invalidator.cpp
@@ -47,10 +47,13 @@
     ALOGD_IF(II_DEBUG, "IdleInvalidator::%s", __FUNCTION__);
 }
 
-int IdleInvalidator::init(InvalidatorHandler reg_handler, void* user_data,
-                         unsigned int idleSleepTime) {
-    ALOGD_IF(II_DEBUG, "IdleInvalidator::%s idleSleepTime %d",
-        __FUNCTION__, idleSleepTime);
+IdleInvalidator::~IdleInvalidator() {
+    if(mTimeoutEventFd >= 0) {
+        close(mTimeoutEventFd);
+    }
+}
+
+int IdleInvalidator::init(InvalidatorHandler reg_handler, void* user_data) {
     mHandler = reg_handler;
     mHwcContext = user_data;
 
@@ -62,34 +65,47 @@
         return -1;
     }
 
-    // Open a sysfs node to send the timeout value to driver.
-    int fd = open(IDLE_TIME_PATH, O_WRONLY);
-    if (fd < 0) {
-        ALOGE ("%s:not able to open %s node %s",
-                __FUNCTION__, IDLE_TIME_PATH, strerror(errno));
+    enum {DEFAULT_IDLE_TIME = 70}; //ms
+    if(not setIdleTimeout(DEFAULT_IDLE_TIME)) {
         close(mTimeoutEventFd);
         mTimeoutEventFd = -1;
         return -1;
     }
-    char strSleepTime[64];
-    snprintf(strSleepTime, sizeof(strSleepTime), "%d", idleSleepTime);
-    // Notify driver about the timeout value
-    ssize_t len = pwrite(fd, strSleepTime, strlen(strSleepTime), 0);
-    if(len < -1) {
-        ALOGE ("%s:not able to write into %s node %s",
-                __FUNCTION__, IDLE_TIME_PATH, strerror(errno));
-        close(mTimeoutEventFd);
-        mTimeoutEventFd = -1;
-        close(fd);
-        return -1;
-    }
-    close(fd);
 
     //Triggers the threadLoop to run, if not already running.
     run(threadName, android::PRIORITY_LOWEST);
     return 0;
 }
 
+bool IdleInvalidator::setIdleTimeout(const uint32_t& timeout) {
+    ALOGD_IF(II_DEBUG, "IdleInvalidator::%s timeout %d",
+            __FUNCTION__, timeout);
+
+    // Open a sysfs node to send the timeout value to driver.
+    int fd = open(IDLE_TIME_PATH, O_WRONLY);
+
+    if (fd < 0) {
+        ALOGE ("%s:Unable to open %s node %s",
+                __FUNCTION__, IDLE_TIME_PATH, strerror(errno));
+        return false;
+    }
+
+    char strSleepTime[64];
+    snprintf(strSleepTime, sizeof(strSleepTime), "%d", timeout);
+
+    // Notify driver about the timeout value
+    ssize_t len = pwrite(fd, strSleepTime, strlen(strSleepTime), 0);
+    if(len < -1) {
+        ALOGE ("%s:Unable to write into %s node %s",
+                __FUNCTION__, IDLE_TIME_PATH, strerror(errno));
+        close(fd);
+        return false;
+    }
+
+    close(fd);
+    return true;
+}
+
 bool IdleInvalidator::threadLoop() {
     ALOGD_IF(II_DEBUG, "IdleInvalidator::%s", __FUNCTION__);
     struct pollfd pFd;
diff --git a/libqdutils/idle_invalidator.h b/libqdutils/idle_invalidator.h
index a881c4b..52334a0 100644
--- a/libqdutils/idle_invalidator.h
+++ b/libqdutils/idle_invalidator.h
@@ -37,16 +37,18 @@
 typedef void (*InvalidatorHandler)(void*);
 
 class IdleInvalidator : public android::Thread {
+    IdleInvalidator();
     void *mHwcContext;
     int mTimeoutEventFd;
     static InvalidatorHandler mHandler;
     static android::sp<IdleInvalidator> sInstance;
 
-    public:
-    IdleInvalidator();
+public:
+    ~IdleInvalidator();
     /* init timer obj */
-    int init(InvalidatorHandler reg_handler, void* user_data, unsigned int
-             idleSleepTime);
+    int init(InvalidatorHandler reg_handler, void* user_data);
+    bool setIdleTimeout(const uint32_t& timeout);
+
     /*Overrides*/
     virtual bool        threadLoop();
     virtual int         readyToRun();
diff --git a/libqservice/IQService.h b/libqservice/IQService.h
index 30c064e..3be20f1 100644
--- a/libqservice/IQService.h
+++ b/libqservice/IQService.h
@@ -53,6 +53,7 @@
         SET_WFD_STATUS,          // Set if wfd connection is on/off
         SET_VIEW_FRAME,          // Set view frame of display
         DYNAMIC_DEBUG,           // Enable more logging on the fly
+        SET_IDLE_TIMEOUT,        // Set idle timeout for GPU fallback
         COMMAND_LIST_END = 400,
     };