• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "healthd-manta"
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <healthd.h>
21 #include <time.h>
22 #include <unistd.h>
23 #include <batteryservice/BatteryService.h>
24 #include <cutils/klog.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 
28 #define POWER_SUPPLY_SUBSYSTEM "power_supply"
29 #define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
30 
31 #define DS2784_PATH POWER_SUPPLY_SYSFS_PATH "/ds2784-fuelgauge"
32 
33 using namespace android;
34 
35 #define TEMP_HIGH_THRESHOLD     600     /* 60C */
36 #define TEMP_HIGH_RECOVERY      420     /* 42C */
37 #define TEMP_LOW_RECOVERY       0       /*  0C */
38 #define TEMP_LOW_THRESHOLD      -50     /* -5C */
39 
40 #define FULL_CHARGING_TIME      (12 * 60 * 60)
41 #define RECHARGING_TIME         (2 * 60 * 60)
42 #define RECHARGING_VOLTAGE      (4250)
43 
44 static bool manta_bat_recharging;
45 static time_t manta_bat_charging_start_time;
46 
47 static unsigned int manta_bat_health = BATTERY_HEALTH_GOOD;
48 static unsigned int manta_bat_charging_status = BATTERY_STATUS_DISCHARGING;
49 static bool manta_bat_batterypresent;
50 
51 static int charge_enabled_fd;
52 
manta_bat_check_temp(struct BatteryProperties * props)53 static void manta_bat_check_temp(struct BatteryProperties *props)
54 {
55     if (props->chargerAcOnline == false &&
56         props->chargerUsbOnline == false)
57         return;
58 
59     if (props->batteryTemperature >= TEMP_HIGH_THRESHOLD) {
60         if (manta_bat_health != BATTERY_HEALTH_OVERHEAT &&
61             manta_bat_health != BATTERY_HEALTH_UNSPECIFIED_FAILURE) {
62                 KLOG_INFO(LOG_TAG,
63                           "battery overheat (%d), charging unavailable\n",
64                           props->batteryTemperature);
65                 manta_bat_health = BATTERY_HEALTH_OVERHEAT;
66         }
67     } else if (props->batteryTemperature <= TEMP_HIGH_RECOVERY &&
68                props->batteryTemperature >= TEMP_LOW_RECOVERY) {
69         if (manta_bat_health == BATTERY_HEALTH_OVERHEAT ||
70             manta_bat_health == BATTERY_HEALTH_COLD) {
71             KLOG_INFO(LOG_TAG,
72                       "battery recovery (%d), charging available\n",
73                       props->batteryTemperature);
74             manta_bat_health = BATTERY_HEALTH_GOOD;
75         }
76     } else if (props->batteryTemperature <= TEMP_LOW_THRESHOLD) {
77         if (manta_bat_health != BATTERY_HEALTH_COLD &&
78             manta_bat_health != BATTERY_HEALTH_UNSPECIFIED_FAILURE) {
79             KLOG_INFO(LOG_TAG,
80                       "battery cold (%d), charging unavailable\n",
81                       props->batteryTemperature);
82             manta_bat_health = BATTERY_HEALTH_COLD;
83         }
84     }
85 }
86 
manta_bat_set_charge_time(bool enable)87 static void manta_bat_set_charge_time(bool enable)
88 {
89     if (enable && !manta_bat_charging_start_time)
90         time(&manta_bat_charging_start_time);
91     else if (!enable)
92         manta_bat_charging_start_time = 0;
93 }
94 
manta_bat_enable_charging(bool enable)95 static void manta_bat_enable_charging(bool enable)
96 {
97     int ret;
98     char val[20];
99 
100     if (enable && (manta_bat_health != BATTERY_HEALTH_GOOD)) {
101         manta_bat_charging_status = BATTERY_STATUS_NOT_CHARGING;
102         return;
103     }
104 
105     if (charge_enabled_fd < 0)
106         goto err;
107 
108     snprintf(val, sizeof(val), "%d", enable);
109     ret = write(charge_enabled_fd, val, strlen(val));
110     if (ret == -1) {
111         KLOG_ERROR(LOG_TAG, "charge_enabled write error; errno=%d\n", errno);
112         goto err;
113     }
114 
115     manta_bat_set_charge_time(enable);
116     KLOG_INFO(LOG_TAG, "battery charge enable=%d\n", enable);
117     return;
118 
119 err:
120     if (enable)
121         manta_bat_charging_status = BATTERY_STATUS_NOT_CHARGING;
122 }
123 
manta_bat_charge_timeout(time_t timeout)124 static bool manta_bat_charge_timeout(time_t timeout)
125 {
126     if (!manta_bat_charging_start_time)
127         return false;
128 
129     return time(NULL) >= manta_bat_charging_start_time + timeout;
130 }
131 
manta_bat_set_full(void)132 static void manta_bat_set_full(void)
133 {
134     KLOG_INFO(LOG_TAG, "battery full\n");
135     manta_bat_charging_status = BATTERY_STATUS_FULL;
136     manta_bat_enable_charging(false);
137     manta_bat_recharging = false;
138 }
139 
manta_bat_charging_timer(struct BatteryProperties * props)140 static void manta_bat_charging_timer(struct BatteryProperties *props)
141 {
142     if (!manta_bat_charging_start_time &&
143         manta_bat_charging_status == BATTERY_STATUS_CHARGING) {
144         KLOG_WARNING("battery charging timer not started, starting\n");
145         manta_bat_enable_charging(true);
146         manta_bat_recharging = true;
147     } else if (!manta_bat_charging_start_time) {
148         return;
149     }
150 
151     if (manta_bat_charge_timeout(manta_bat_recharging ?
152                                  RECHARGING_TIME : FULL_CHARGING_TIME)) {
153         KLOG_INFO(LOG_TAG, "battery charging timer expired\n");
154         if (props->batteryVoltage > RECHARGING_VOLTAGE &&
155             props->batteryLevel == 100) {
156             manta_bat_set_full();
157         } else {
158             manta_bat_enable_charging(false);
159             manta_bat_recharging = false;
160             manta_bat_charging_start_time = 0;
161         }
162     }
163 }
164 
manta_bat_check_charge_source_changed(struct BatteryProperties * props)165 static void manta_bat_check_charge_source_changed(
166     struct BatteryProperties *props)
167 {
168     if (props->chargerUsbOnline || props->chargerAcOnline) {
169         if (manta_bat_charging_status == BATTERY_STATUS_CHARGING ||
170             (manta_bat_charging_status == BATTERY_STATUS_FULL &&
171              manta_bat_recharging))
172             return;
173 
174         /*
175          * If charging status indicates a charger was already
176          * connected prior to this and the status is something
177          * other than charging ("full" or "not-charging"), leave
178          * the status alone.
179          */
180         if (manta_bat_charging_status == BATTERY_STATUS_DISCHARGING ||
181             manta_bat_charging_status == BATTERY_STATUS_UNKNOWN)
182             manta_bat_charging_status = BATTERY_STATUS_CHARGING;
183 
184         /*
185          * Don't re-enable charging if the battery is full and we
186          * are not actively re-charging it, or if "not-charging"
187          * status is set.
188          */
189         if (!(manta_bat_charging_status == BATTERY_STATUS_FULL
190               && !manta_bat_recharging) &&
191             manta_bat_charging_status != BATTERY_STATUS_NOT_CHARGING)
192             manta_bat_enable_charging(true);
193     } else if (manta_bat_charging_status == BATTERY_STATUS_CHARGING ||
194                manta_bat_charging_status == BATTERY_STATUS_NOT_CHARGING ||
195                manta_bat_charging_status == BATTERY_STATUS_FULL) {
196         manta_bat_charging_status = BATTERY_STATUS_DISCHARGING;
197         manta_bat_enable_charging(false);
198         manta_bat_health = BATTERY_HEALTH_GOOD;
199         manta_bat_recharging = false;
200         manta_bat_charging_start_time = 0;
201     }
202 }
203 
manta_bat_monitor(struct BatteryProperties * props)204 static void manta_bat_monitor(struct BatteryProperties *props)
205 {
206     unsigned int old_bat_health = manta_bat_health;
207 
208     if (manta_bat_batterypresent) {
209         manta_bat_check_temp(props);
210     } else {
211          props->batteryTemperature = 42;  /* 4.2C */
212          props->batteryVoltage = 4242; /* 4242mV */
213          props->batteryLevel = 42;        /* 42% */
214          props->batteryCurrentNow = 42000;/* 42mA */
215     }
216 
217     if (props->batteryStatus == BATTERY_STATUS_FULL &&
218         (manta_bat_charging_status == BATTERY_STATUS_CHARGING ||
219          manta_bat_recharging)) {
220             manta_bat_set_full();
221     }
222 
223     manta_bat_check_charge_source_changed(props);
224 
225     switch (manta_bat_charging_status) {
226     case BATTERY_STATUS_FULL:
227         if (props->batteryVoltage < RECHARGING_VOLTAGE &&
228             !manta_bat_recharging) {
229             KLOG_INFO(LOG_TAG, "start recharging, v=%d\n",
230                       props->batteryVoltage);
231             manta_bat_recharging = true;
232             manta_bat_enable_charging(true);
233         }
234         break;
235     case BATTERY_STATUS_DISCHARGING:
236         break;
237     case BATTERY_STATUS_CHARGING:
238         switch (manta_bat_health) {
239         case BATTERY_HEALTH_OVERHEAT:
240         case BATTERY_HEALTH_COLD:
241         case BATTERY_HEALTH_OVER_VOLTAGE:
242         case BATTERY_HEALTH_DEAD:
243         case BATTERY_HEALTH_UNSPECIFIED_FAILURE:
244             manta_bat_charging_status = BATTERY_STATUS_NOT_CHARGING;
245             manta_bat_enable_charging(false);
246             KLOG_INFO(LOG_TAG, "Not charging, health=%d\n",
247                       manta_bat_health);
248             break;
249         default:
250             break;
251         }
252         break;
253     case BATTERY_STATUS_NOT_CHARGING:
254         if (old_bat_health != BATTERY_HEALTH_GOOD &&
255             manta_bat_health == BATTERY_HEALTH_GOOD) {
256                 KLOG_INFO(LOG_TAG, "battery health recovered\n");
257 
258                 if (props->chargerUsbOnline || props->chargerAcOnline) {
259                     manta_bat_enable_charging(true);
260                     manta_bat_charging_status = BATTERY_STATUS_CHARGING;
261                 } else {
262                     manta_bat_charging_status =
263                             BATTERY_STATUS_DISCHARGING;
264                 }
265         }
266         break;
267     default:
268         break;
269     }
270 
271     manta_bat_charging_timer(props);
272 
273     // set health and status according to our state, hardcode invariants
274     props->batteryHealth = manta_bat_health;
275     props->batteryStatus = manta_bat_charging_status;
276     props->batteryTechnology = "Li-ion";
277     props->batteryPresent = manta_bat_batterypresent;
278 }
279 
healthd_board_battery_update(struct BatteryProperties * props)280 int healthd_board_battery_update(struct BatteryProperties *props)
281 {
282     manta_bat_monitor(props);
283 
284     // return 0 to log periodic polled battery status to kernel log
285     return 0;
286 }
287 
healthd_board_init(struct healthd_config * config)288 void healthd_board_init(struct healthd_config *config)
289 {
290     charge_enabled_fd = open(POWER_SUPPLY_SYSFS_PATH
291                              "/manta-battery/charge_enabled", O_WRONLY);
292     if (charge_enabled_fd == -1)
293         KLOG_ERROR(LOG_TAG, "open manta-battery/charge_enabled failed"
294                    "; errno=%d\n", errno);
295 
296     config->batteryCurrentNowPath = DS2784_PATH "/current_now";
297 
298     if (access(config->batteryCurrentNowPath.string(), R_OK) == 0) {
299         manta_bat_batterypresent = true;
300     } else {
301         KLOG_INFO("Missing battery, using fake battery data\n");
302         config->batteryCurrentNowPath.clear();
303     }
304 }
305