diff --git a/healthd/Android.mk b/healthd/Android.mk index d725f735e..5f10f1e8c 100644 --- a/healthd/Android.mk +++ b/healthd/Android.mk @@ -96,7 +96,7 @@ ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),) LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW) endif -LOCAL_STATIC_LIBRARIES := \ +CHARGER_STATIC_LIBRARIES := \ android.hardware.health@2.0-impl \ android.hardware.health@2.0 \ android.hardware.health@1.0 \ @@ -114,6 +114,8 @@ LOCAL_STATIC_LIBRARIES := \ libm \ libc \ +LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES) + ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true) LOCAL_STATIC_LIBRARIES += \ libminui \ @@ -134,6 +136,21 @@ LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \ include $(BUILD_EXECUTABLE) +include $(CLEAR_VARS) +LOCAL_MODULE := charger_test +LOCAL_MODULE_TAGS := optional +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Wall -Werror -DCHARGER_TEST -DCHARGER_NO_UI +LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES) +LOCAL_SRC_FILES := \ + charger.cpp \ + charger_test.cpp \ + +include $(BUILD_EXECUTABLE) + +CHARGER_STATIC_LIBRARIES := + ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true) define _add-charger-image include $$(CLEAR_VARS) diff --git a/healthd/charger.cpp b/healthd/charger.cpp index ede14f2a1..43e7fd5b8 100644 --- a/healthd/charger.cpp +++ b/healthd/charger.cpp @@ -79,7 +79,7 @@ static void healthd_mode_nop_battery_update( struct android::BatteryProperties* /*props*/) { } -int main(int argc, char **argv) { +int healthd_charger_main(int argc, char** argv) { int ch; healthd_mode_ops = &charger_ops; @@ -103,3 +103,9 @@ int main(int argc, char **argv) { return healthd_main(); } + +#ifndef CHARGER_TEST +int main(int argc, char** argv) { + return healthd_charger_main(argc, argv); +} +#endif diff --git a/healthd/charger_test.cpp b/healthd/charger_test.cpp new file mode 100644 index 000000000..acc0f5bb0 --- /dev/null +++ b/healthd/charger_test.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2017 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 "charger_test" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define LOG_THIS(fmt, ...) \ + ALOGE(fmt, ##__VA_ARGS__); \ + printf(fmt "\n", ##__VA_ARGS__); + +template +class Atomic { + public: + Atomic(T&& init) : mValue(std::move(init)) {} + void set(T&& newVal) { + { + std::lock_guard lock(mMutex); + mValue = std::move(newVal); + } + mChanged.notify_all(); + } + bool waitFor(long ms, const T& expectVal) { + std::unique_lock lock(mMutex); + return mChanged.wait_for(lock, std::chrono::milliseconds(ms), + [this, &expectVal] { return mValue == expectVal; }); + } + private: + std::mutex mMutex; + std::condition_variable mChanged; + T mValue; +}; + +Atomic& getUpdateNotifier() { + static Atomic val(false); + return val; +} + +int energyCounter(int64_t* counter) { + *counter = 0xEC12345; + return 0; +} + +const char* createFile(const char* path, const char* content) { + std::ofstream stream(path); + if (!stream.is_open()) { + LOG_THIS("Cannot create file %s", path); + return NULL; + } + stream << content << std::endl; + stream.close(); + return path; +} + +std::string openToString(const char* path) { + std::ifstream stream(path); + if (!stream.is_open()) { + LOG_THIS("Cannot open file %s", path); + return ""; + } + return std::string(std::istreambuf_iterator(stream), std::istreambuf_iterator()); +} + +int expectContains(const std::string& content, const std::vector& fields) { + int status = 0; + for (const auto& field : fields) { + auto pos = content.find(field); + if (pos == std::string::npos) { + LOG_THIS("Cannot find substr '%s'", field.c_str()); + status = 1; + } + } + return status; +} + +void healthd_board_init(struct healthd_config* config) { + config->periodic_chores_interval_fast = 60; + config->periodic_chores_interval_slow = 600; + + config->batteryStatusPath = createFile("/data/local/tmp/batteryStatus", "Not charging"); + config->batteryHealthPath = createFile("/data/local/tmp/batteryHealth", "Unspecified failure"); + config->batteryPresentPath = createFile("/data/local/tmp/batteryPresent", "1"); + config->batteryCapacityPath = createFile("/data/local/tmp/batteryCapacity", "47"); + config->batteryVoltagePath = createFile("/data/local/tmp/batteryVoltage", "45000"); + config->batteryTemperaturePath = createFile("/data/local/tmp/batteryTemperature", "987"); + config->batteryTechnologyPath = createFile("/data/local/tmp/batteryTechnology", "NiCd"); + config->batteryCurrentNowPath = createFile("/data/local/tmp/batteryCurrentNow", "99000"); + config->batteryCurrentAvgPath = createFile("/data/local/tmp/batteryCurrentAvg", "98000"); + config->batteryChargeCounterPath = createFile("/data/local/tmp/batteryChargeCounter", "600"); + config->batteryFullChargePath = createFile("/data/local/tmp/batteryFullCharge", "3515547"); + config->batteryCycleCountPath = createFile("/data/local/tmp/batteryCycleCount", "77"); + + config->energyCounter = energyCounter; + config->boot_min_cap = 50; + config->screen_on = NULL; +} + +int healthd_board_battery_update(struct android::BatteryProperties*) { + getUpdateNotifier().set(true /* updated */); + + // return 0 to log periodic polled battery status to kernel log + return 0; +} + +extern int healthd_charger_main(int argc, char** argv); + +int main(int argc, char** argv) { + const char* dumpFile = "/data/local/tmp/dump.txt"; + + std::thread bgThread([=] { + healthd_charger_main(argc, argv); + }); + + // wait for healthd_init to finish + if (!getUpdateNotifier().waitFor(1000 /* wait ms */, true /* updated */)) { + LOG_THIS("Time out."); + exit(1); + } + + int fd = creat(dumpFile, S_IRUSR | S_IWUSR); + if (fd < 0) { + exit(errno); + } + healthd_dump_battery_state(fd); + close(fd); + + std::string content = openToString(dumpFile); + int status = expectContains(content, { + "status: 4", + "health: 6", + "present: 1", + "level: 47", + "voltage: 45", + "temp: 987", + "current now: 99000", + "current avg: 98000", + "charge counter: 600", + "current now: 99", + "cycle count: 77", + "Full charge: 3515547" + }); + + if (status == 0) { + LOG_THIS("Test success."); + } else { + LOG_THIS("Actual dump:\n%s", content.c_str()); + } + + exit(status); // force bgThread to exit +}