healthd: Add battery max current check to adjust SoC.

Examine battery's max current (imax) and once it recedes below nominal
levels adjust state-of-charge (SoC) accordingly to hasten shutdown.

BUG=chrome-os-partner:46340
TEST=manual, with battery at low charge (4%) start full-screen streaming
video.  Both video/audio play smoothly until shutdown which occurs when
battery imax < 5A.

From healthd,
[  529.110280] healthd-dragon: imax=7290 soc=3
[  529.117847] healthd: battery l=3 v=3518 t=28.6 h=2 st=3 c=-1658 chg= 2016-01-12 14:19:20.633949198 UTC
[  575.223618] healthd-dragon: imax=6731 soc=2
[  575.232233] healthd: battery l=2 v=3516 t=28.7 h=2 st=3 c=-1454 chg= 2016-01-12 14:20:06.748372514 UTC
[  635.326417] healthd-dragon: imax=6059 soc=2
[  635.334322] healthd: battery l=2 v=3492 t=28.6 h=2 st=3 c=-1494 chg= 2016-01-12 14:21:06.850360251 UTC
[  695.422510] healthd-dragon: imax=5542 soc=1
[  695.429435] healthd: battery l=1 v=3479 t=28.6 h=2 st=3 c=-1364 chg= 2016-01-12 14:22:06.945518249 UTC
[  755.533159] healthd-dragon: imax=5139 soc=1
[  755.539732] healthd: battery l=1 v=3453 t=28.6 h=2 st=3 c=-1527 chg= 2016-01-12 14:23:07.055848435 UTC
[  785.806587] healthd-dragon: imax=4880 soc=0
[  785.812769] healthd: battery l=0 v=3436 t=28.6 h=2 st=3 c=-1499 chg= 2016-01-12 14:23:37.328847017 UTC
<shutdown occurs>

Change-Id: I001615348d1783c10a54d397d09e4c8f04fa78de
diff --git a/BoardConfig.mk b/BoardConfig.mk
index 381ce1b..ae27dc9 100644
--- a/BoardConfig.mk
+++ b/BoardConfig.mk
@@ -125,4 +125,4 @@
   BOARD_SUPPORT_ROLLBACK_PROTECTION := true
 endif
 
-BOARD_HAL_STATIC_LIBRARIES := libdumpstate.dragon
+BOARD_HAL_STATIC_LIBRARIES := libdumpstate.dragon libhealthd.dragon
diff --git a/health/Android.mk b/health/Android.mk
new file mode 100644
index 0000000..50d1cc9
--- /dev/null
+++ b/health/Android.mk
@@ -0,0 +1,26 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := system/core/healthd
+
+LOCAL_SRC_FILES := healthd-dragon.cpp
+
+LOCAL_MODULE := libhealthd.dragon
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/health/healthd-dragon.cpp b/health/healthd-dragon.cpp
new file mode 100644
index 0000000..d28f439
--- /dev/null
+++ b/health/healthd-dragon.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "healthd-dragon"
+#include <healthd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <cutils/klog.h>
+#include <sys/types.h>
+
+#define PSU_SYSFS_PATH "/sys/class/power_supply/bq27742-0"
+#define PSU_SYSFS_MAX_CURRENT_PATH PSU_SYSFS_PATH "/current_max"
+#define BATTERY_CRITICAL_LOW_CAP 10
+#define BATTERY_CRITICAL_LOW_IMAX_MA 5000
+#define BATTERY_MAX_IMAX_MA          9000
+
+using namespace android;
+
+static int read_sysfs(const char *path, char *buf, size_t size) {
+    char *cp = NULL;
+
+    int fd = open(path, O_RDONLY);
+    if (fd == -1) {
+        KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path);
+        return -1;
+    }
+
+    ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
+    if (count > 0)
+        cp = (char *)memrchr(buf, '\n', count);
+
+    if (cp)
+        *cp = '\0';
+    else
+        buf[0] = '\0';
+
+    close(fd);
+    return count;
+}
+
+static int read_current_max_ma() {
+    const int SIZE = 10;
+    char buf[SIZE];
+    if (read_sysfs(PSU_SYSFS_MAX_CURRENT_PATH, buf, SIZE) > 0)
+        return atoi(buf) / 1000;
+
+    return 0;
+}
+
+static void dragon_soc_adjust(struct BatteryProperties *props)
+{
+    int soc;
+    int current_max_ma;
+
+    soc = props->batteryLevel;
+    /*
+     * if not charging and State-Of-Charge (soc) is below
+     * BATTERY_CRITICAL_LOW_CAP shrink soc based on imax value
+     */
+    if ((soc < BATTERY_CRITICAL_LOW_CAP) &&
+        ((props->batteryStatus == BATTERY_STATUS_DISCHARGING) ||
+         (props->batteryStatus == BATTERY_STATUS_NOT_CHARGING) ||
+         (props->batteryStatus == BATTERY_STATUS_UNKNOWN))) {
+
+        current_max_ma = read_current_max_ma();
+        if (current_max_ma == 0)
+            /*
+             * Either it failed to read sysfs or its really zero.  In either
+             * case lets just warn so logs will identify for further debug.
+             */
+            KLOG_WARNING(LOG_TAG, "imax=0\n");
+        else if (current_max_ma < BATTERY_CRITICAL_LOW_IMAX_MA)
+            /* force shutdown */
+            soc = 0;
+        else if (current_max_ma < BATTERY_MAX_IMAX_MA)
+            /* decrease soc towards zero */
+            soc = soc * current_max_ma / BATTERY_MAX_IMAX_MA;
+
+        KLOG_INFO(LOG_TAG, "imax=%d soc=%d\n", current_max_ma, soc);
+    }
+    props->batteryLevel = soc;
+}
+
+int healthd_board_battery_update(struct BatteryProperties *props)
+{
+
+    dragon_soc_adjust(props);
+
+    // return 0 to log periodic polled battery status to kernel log
+    return 0;
+}
+
+void healthd_board_init(struct healthd_config *config) {}