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