/* * 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 <android/log.h> #include <chrono> #include <condition_variable> #include <fstream> #include <iostream> #include <mutex> #include <streambuf> #include <string> #include <thread> #include <vector> #include <health2/Health.h> #define LOG_THIS(fmt, ...) \ ALOGE(fmt, ##__VA_ARGS__); \ printf(fmt "\n", ##__VA_ARGS__); template <typename T> class Atomic { public: Atomic(T&& init) : mValue(std::move(init)) {} void set(T&& newVal) { { std::lock_guard<std::mutex> lock(mMutex); mValue = std::move(newVal); } mChanged.notify_all(); } bool waitFor(long ms, const T& expectVal) { std::unique_lock<std::mutex> 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<bool>& getUpdateNotifier() { static Atomic<bool> 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<char>(stream), std::istreambuf_iterator<char>()); } int expectContains(const std::string& content, const std::vector<std::string>& 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; } ::android::hardware::hidl_handle createHidlHandle(const char* filepath) { int fd = creat(filepath, S_IRUSR | S_IWUSR); if (fd < 0) return {}; native_handle_t* nativeHandle = native_handle_create(1, 0); nativeHandle->data[0] = fd; ::android::hardware::hidl_handle handle; handle.setTo(nativeHandle, true /* shouldOwn */); return handle; } 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) { using android::hardware::health::V2_0::implementation::Health; 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); } Health::getImplementation()->debug(createHidlHandle(dumpFile), {} /* options */); 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 }