1 /*
2  * Copyright (C) 2017 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 "charger_test"
18 #include <android/log.h>
19 
20 #include <chrono>
21 #include <condition_variable>
22 #include <fstream>
23 #include <iostream>
24 #include <memory>
25 #include <mutex>
26 #include <streambuf>
27 #include <string>
28 #include <thread>
29 #include <vector>
30 
31 #include <health/utils.h>
32 #include <health2impl/Health.h>
33 
34 #include "healthd_mode_charger_hidl.h"
35 
36 using android::hardware::health::InitHealthdConfig;
37 using android::hardware::health::V2_1::HealthInfo;
38 using android::hardware::health::V2_1::IHealth;
39 using android::hardware::health::V2_1::implementation::Health;
40 
41 #define LOG_THIS(fmt, ...)     \
42     ALOGE(fmt, ##__VA_ARGS__); \
43     printf(fmt "\n", ##__VA_ARGS__);
44 
45 template <typename T>
46 class Atomic {
47   public:
Atomic(T && init)48     Atomic(T&& init) : mValue(std::move(init)) {}
set(T && newVal)49     void set(T&& newVal) {
50         {
51             std::lock_guard<std::mutex> lock(mMutex);
52             mValue = std::move(newVal);
53         }
54         mChanged.notify_all();
55     }
waitFor(long ms,const T & expectVal)56     bool waitFor(long ms, const T& expectVal) {
57         std::unique_lock<std::mutex> lock(mMutex);
58         return mChanged.wait_for(lock, std::chrono::milliseconds(ms),
59                                  [this, &expectVal] { return mValue == expectVal; });
60     }
61   private:
62     std::mutex mMutex;
63     std::condition_variable mChanged;
64     T mValue;
65 };
66 
getUpdateNotifier()67 Atomic<bool>& getUpdateNotifier() {
68     static Atomic<bool> val(false);
69     return val;
70 }
71 
energyCounter(int64_t * counter)72 int energyCounter(int64_t* counter) {
73     *counter = 0xEC12345;
74     return 0;
75 }
76 
createFile(const char * path,const char * content)77 const char* createFile(const char* path, const char* content) {
78     std::ofstream stream(path);
79     if (!stream.is_open()) {
80         LOG_THIS("Cannot create file %s", path);
81         return NULL;
82     }
83     stream << content << std::endl;
84     stream.close();
85     return path;
86 }
87 
openToString(const char * path)88 std::string openToString(const char* path) {
89     std::ifstream stream(path);
90     if (!stream.is_open()) {
91         LOG_THIS("Cannot open file %s", path);
92         return "";
93     }
94     return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
95 }
96 
expectContains(const std::string & content,const std::vector<std::string> & fields)97 int expectContains(const std::string& content, const std::vector<std::string>& fields) {
98     int status = 0;
99     for (const auto& field : fields) {
100         auto pos = content.find(field);
101         if (pos == std::string::npos) {
102             LOG_THIS("Cannot find substr '%s'", field.c_str());
103             status = 1;
104         }
105     }
106     return status;
107 }
108 
createHidlHandle(const char * filepath)109 ::android::hardware::hidl_handle createHidlHandle(const char* filepath) {
110     int fd = creat(filepath, S_IRUSR | S_IWUSR);
111     if (fd < 0) return {};
112     native_handle_t* nativeHandle = native_handle_create(1, 0);
113     nativeHandle->data[0] = fd;
114     ::android::hardware::hidl_handle handle;
115     handle.setTo(nativeHandle, true /* shouldOwn */);
116     return handle;
117 }
118 
healthd_board_init(struct healthd_config * config)119 void healthd_board_init(struct healthd_config* config) {
120     config->periodic_chores_interval_fast = 60;
121     config->periodic_chores_interval_slow = 600;
122 
123     config->batteryStatusPath = createFile("/data/local/tmp/batteryStatus", "Not charging");
124     config->batteryHealthPath = createFile("/data/local/tmp/batteryHealth", "Unspecified failure");
125     config->batteryPresentPath = createFile("/data/local/tmp/batteryPresent", "1");
126     config->batteryCapacityPath = createFile("/data/local/tmp/batteryCapacity", "47");
127     config->batteryVoltagePath = createFile("/data/local/tmp/batteryVoltage", "45000");
128     config->batteryTemperaturePath = createFile("/data/local/tmp/batteryTemperature", "987");
129     config->batteryTechnologyPath = createFile("/data/local/tmp/batteryTechnology", "NiCd");
130     config->batteryCurrentNowPath = createFile("/data/local/tmp/batteryCurrentNow", "99000");
131     config->batteryCurrentAvgPath = createFile("/data/local/tmp/batteryCurrentAvg", "98000");
132     config->batteryChargeCounterPath = createFile("/data/local/tmp/batteryChargeCounter", "600");
133     config->batteryFullChargePath = createFile("/data/local/tmp/batteryFullCharge", "3515547");
134     config->batteryCycleCountPath = createFile("/data/local/tmp/batteryCycleCount", "77");
135 
136     config->energyCounter = energyCounter;
137     config->boot_min_cap = 50;
138     config->screen_on = NULL;
139 }
140 
141 class TestHealth : public Health {
142   protected:
143     using Health::Health;
UpdateHealthInfo(HealthInfo *)144     void UpdateHealthInfo(HealthInfo*) override { getUpdateNotifier().set(true /* updated */); }
145 };
146 
main(int,char **)147 int main(int /*argc*/, char** /*argv*/) {
148     const char* dumpFile = "/data/local/tmp/dump.txt";
149 
150     auto config = std::make_unique<healthd_config>();
151     InitHealthdConfig(config.get());
152     healthd_board_init(config.get());
153     sp<IHealth> passthrough = new TestHealth(std::move(config));
154 
155     std::thread bgThread([=] {
156         android::ChargerHidl charger(passthrough);
157         charger.StartLoop();
158     });
159 
160     // wait for healthd_init to finish
161     if (!getUpdateNotifier().waitFor(1000 /* wait ms */, true /* updated */)) {
162         LOG_THIS("Time out.");
163         exit(1);
164     }
165 
166     passthrough->debug(createHidlHandle(dumpFile), {} /* options */);
167 
168     std::string content = openToString(dumpFile);
169     int status = expectContains(content, {
170         "status: 4",
171         "health: 6",
172         "present: 1",
173         "level: 47",
174         "voltage: 45",
175         "temp: 987",
176         "current now: 99000",
177         "current avg: 98000",
178         "charge counter: 600",
179         "current now: 99",
180         "cycle count: 77",
181         "Full charge: 3515547"
182     });
183 
184     if (status == 0) {
185         LOG_THIS("Test success.");
186     } else {
187         LOG_THIS("Actual dump:\n%s", content.c_str());
188     }
189 
190     exit(status);  // force bgThread to exit
191 }
192