1 /*
2  * Copyright (C) 2018 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 #define LOG_TAG "[email protected]"
17 #include <android-base/logging.h>
18 
19 #include <android-base/file.h>
20 #include <android-base/parseint.h>
21 #include <android-base/strings.h>
22 #include <android/hardware/health/translate-ndk.h>
23 #include <health-impl/Health.h>
24 #include <health/utils.h>
25 
26 // Recovery doesn't have libpixelhealth and charger mode
27 #ifndef __ANDROID_RECOVERY__
28 #include <health-impl/ChargerUtils.h>
29 #include <pixelhealth/BatteryDefender.h>
30 #include <pixelhealth/BatteryMetricsLogger.h>
31 #include <pixelhealth/ChargerDetect.h>
32 #include <pixelhealth/DeviceHealth.h>
33 #include <pixelhealth/LowBatteryShutdownMetrics.h>
34 #endif // !__ANDROID_RECOVERY__
35 
36 #include <chrono>
37 #include <fstream>
38 #include <iomanip>
39 #include <string>
40 #include <vector>
41 
42 namespace {
43 
44 using namespace std::literals;
45 
46 using aidl::android::hardware::health::DiskStats;
47 using aidl::android::hardware::health::HalHealthLoop;
48 using aidl::android::hardware::health::HealthInfo;
49 using aidl::android::hardware::health::StorageInfo;
50 using android::hardware::health::InitHealthdConfig;
51 
52 #ifndef __ANDROID_RECOVERY__
53 using aidl::android::hardware::health::charger::ChargerCallback;
54 using aidl::android::hardware::health::charger::ChargerModeMain;
55 using hardware::google::pixel::health::BatteryDefender;
56 using hardware::google::pixel::health::BatteryMetricsLogger;
57 using hardware::google::pixel::health::DeviceHealth;
58 using hardware::google::pixel::health::LowBatteryShutdownMetrics;
59 using hardware::google::pixel::health::ChargerDetect;
60 
61 #define FG_DIR "/sys/class/power_supply/battery"
62 constexpr char kBatteryResistance[] {FG_DIR "/resistance"};
63 constexpr char kBatteryOCV[] {FG_DIR "/voltage_ocv"};
64 constexpr char kVoltageAvg[] {FG_DIR "/voltage_now"};
65 
66 #define WLC_DIR "/sys/class/power_supply/wireless"
67 
68 static BatteryDefender battDefender(WLC_DIR "/present",
69     "/sys/devices/platform/google,charger/charge_start_level",
70     "/sys/devices/platform/google,charger/charge_stop_level");
71 static BatteryMetricsLogger battMetricsLogger(kBatteryResistance, kBatteryOCV);
72 static LowBatteryShutdownMetrics shutdownMetrics(kVoltageAvg);
73 static DeviceHealth deviceHealth;
74 #endif // !__ANDROID_RECOVERY__
75 
76 #define UFS_DIR "/dev/sys/block/bootdevice"
77 constexpr char kUfsHealthEol[]{UFS_DIR "/health_descriptor/eol_info"};
78 constexpr char kUfsHealthLifetimeA[]{UFS_DIR "/health_descriptor/life_time_estimation_a"};
79 constexpr char kUfsHealthLifetimeB[]{UFS_DIR "/health_descriptor/life_time_estimation_b"};
80 constexpr char kUfsVersion[]{UFS_DIR "/device_descriptor/specification_version"};
81 constexpr char kDiskStatsFile[]{"/sys/block/sda/stat"};
82 
83 static std::string ufs_version;
84 static uint16_t eol;
85 static uint16_t lifetimeA;
86 static uint16_t lifetimeB;
87 static std::chrono::system_clock::time_point ufs_last_query_time;
88 constexpr auto kUfsQueryIntervalHours = std::chrono::hours{24};
89 
90 #ifndef __ANDROID_RECOVERY__
91 static bool needs_wlc_updates = false;
92 constexpr char kWlcCapacity[]{WLC_DIR "/capacity"};
93 #endif // !__ANDROID_RECOVERY__
94 
assert_open(const std::string & path)95 std::ifstream assert_open(const std::string &path) {
96   std::ifstream stream(path);
97   if (!stream.is_open()) {
98     LOG(WARNING) << "Cannot read " << path;
99   }
100   return stream;
101 }
102 
103 template <typename T>
read_value_from_file(const std::string & path,T * field)104 void read_value_from_file(const std::string &path, T *field) {
105   auto stream = assert_open(path);
106   stream.unsetf(std::ios_base::basefield);
107   stream >> *field;
108 }
109 
read_ufs_version(StorageInfo * info)110 void read_ufs_version(StorageInfo *info) {
111   if (ufs_version.empty()) {
112     uint64_t value;
113     read_value_from_file(kUfsVersion, &value);
114     std::stringstream ss;
115     ss << "ufs " << std::hex << value;
116     ufs_version = ss.str();
117     LOG(INFO) << "ufs: " << ufs_version << " detected";
118   }
119   info->version = ufs_version;
120 }
121 
122 #ifdef __ANDROID_RECOVERY__
private_healthd_board_init(struct healthd_config *)123 void private_healthd_board_init(struct healthd_config *) {}
private_healthd_board_battery_update(HealthInfo *)124 int private_healthd_board_battery_update(HealthInfo *) { return 0; }
125 #else // !__ANDROID__RECOVERY__
126 
FileExists(const std::string & filename)127 static bool FileExists(const std::string &filename) {
128   struct stat buffer;
129 
130   return stat(filename.c_str(), &buffer) == 0;
131 }
132 
private_healthd_board_init(struct healthd_config * hc)133 void private_healthd_board_init(struct healthd_config *hc) {
134   std::string tcpmPsyName;
135   ChargerDetect::populateTcpmPsyName(&tcpmPsyName);
136   hc->ignorePowerSupplyNames.push_back(android::String8(tcpmPsyName.c_str()));
137   needs_wlc_updates = FileExists(kWlcCapacity);
138   if (needs_wlc_updates == false) {
139     battDefender.setWirelessNotSupported();
140   }
141 }
142 
private_healthd_board_battery_update(HealthInfo * health_info)143 int private_healthd_board_battery_update(HealthInfo *health_info) {
144   int batt_level;
145   deviceHealth.update(health_info);
146   battMetricsLogger.logBatteryProperties(*health_info);
147   shutdownMetrics.logShutdownVoltage(*health_info);
148   // Allow BatteryDefender to override online properties
149   ChargerDetect::onlineUpdate(health_info);
150   battDefender.update(health_info);
151 
152   batt_level = (health_info->batteryStatus == ::aidl::android::hardware::health::BatteryStatus::FULL) ? 101 : health_info->batteryLevel;
153   if (needs_wlc_updates &&
154       !android::base::WriteStringToFile(std::to_string(batt_level), kWlcCapacity))
155       LOG(INFO) << "Unable to write battery level to wireless capacity";
156 
157   return 0;
158 }
159 #endif // __ANDROID_RECOVERY__
160 
private_get_storage_info(std::vector<StorageInfo> * vec_storage_info)161 void private_get_storage_info(std::vector<StorageInfo> *vec_storage_info) {
162   vec_storage_info->resize(1);
163   StorageInfo *storage_info = &vec_storage_info->at(0);
164 
165   read_ufs_version(storage_info);
166 
167   auto time_now = std::chrono::system_clock::now();
168   auto time_delta = time_now - ufs_last_query_time;
169   auto hoursElapsed = std::chrono::duration_cast<std::chrono::hours>(time_delta);
170   if (hoursElapsed >= kUfsQueryIntervalHours) {
171     ufs_last_query_time = time_now;
172     read_value_from_file(kUfsHealthEol, &eol);
173     read_value_from_file(kUfsHealthLifetimeA, &lifetimeA);
174     read_value_from_file(kUfsHealthLifetimeB, &lifetimeB);
175     LOG(INFO) << "ufs: eol=" << eol << " lifetimeA=" << lifetimeA << " lifetimeB=" << lifetimeB;
176   }
177   storage_info->eol = eol;
178   storage_info->lifetimeA = lifetimeA;
179   storage_info->lifetimeB = lifetimeB;
180 
181   return;
182 }
183 
private_get_disk_stats(std::vector<DiskStats> * vec_stats)184 void private_get_disk_stats(std::vector<DiskStats> *vec_stats) {
185   vec_stats->resize(1);
186   DiskStats *stats = &vec_stats->at(0);
187 
188   auto stream = assert_open(kDiskStatsFile);
189   // Regular diskstats entries
190   stream >> stats->reads >> stats->readMerges >> stats->readSectors >>
191       stats->readTicks >> stats->writes >> stats->writeMerges >>
192       stats->writeSectors >> stats->writeTicks >> stats->ioInFlight >>
193       stats->ioTicks >> stats->ioInQueue;
194   return;
195 }
196 }  // anonymous namespace
197 
198 namespace aidl::android::hardware::health::implementation {
199 class HealthImpl : public Health {
200  public:
HealthImpl(std::string_view instance_name,std::unique_ptr<healthd_config> && config)201   HealthImpl(std::string_view instance_name, std::unique_ptr<healthd_config>&& config)
202     : Health(std::move(instance_name), std::move(config)) {}
203 
204     ndk::ScopedAStatus getDiskStats(std::vector<DiskStats>* out) override;
205     ndk::ScopedAStatus getStorageInfo(std::vector<StorageInfo>* out) override;
206 
207  protected:
208   void UpdateHealthInfo(HealthInfo* health_info) override;
209 
210 };
211 
UpdateHealthInfo(HealthInfo * health_info)212 void HealthImpl::UpdateHealthInfo(HealthInfo* health_info) {
213   private_healthd_board_battery_update(health_info);
214 }
215 
getStorageInfo(std::vector<StorageInfo> * out)216 ndk::ScopedAStatus HealthImpl::getStorageInfo(std::vector<StorageInfo>* out)
217 {
218   private_get_storage_info(out);
219   if (out->empty()) {
220     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
221   }
222   return ndk::ScopedAStatus::ok();
223 }
224 
getDiskStats(std::vector<DiskStats> * out)225 ndk::ScopedAStatus HealthImpl::getDiskStats(std::vector<DiskStats>* out)
226 {
227   private_get_disk_stats(out);
228   if (out->empty()) {
229     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
230   }
231   return ndk::ScopedAStatus::ok();
232 }
233 
234 }  // namespace aidl::android::hardware::health::implementation
235 
main(int argc,char ** argv)236 int main(int argc, char **argv) {
237   using ::aidl::android::hardware::health::implementation::HealthImpl;
238 
239   // Use kernel logging in recovery
240 #ifdef __ANDROID_RECOVERY__
241   android::base::InitLogging(argv, android::base::KernelLogger);
242 #endif
243 
244   auto config = std::make_unique<healthd_config>();
245   InitHealthdConfig(config.get());
246 
247   private_healthd_board_init(config.get());
248 
249   auto binder =
250       ndk::SharedRefBase::make<HealthImpl>("default"sv, std::move(config));
251 
252   if (argc >= 2 && argv[1] == "--charger"sv) {
253     // In regular mode, start charger UI.
254 #ifndef __ANDROID_RECOVERY__
255     LOG(INFO) << "Starting charger mode with UI.";
256     return ChargerModeMain(binder, std::make_shared<ChargerCallback>(binder));
257 #endif
258     // In recovery, ignore --charger arg.
259     LOG(INFO) << "Starting charger mode without UI.";
260   } else {
261     LOG(INFO) << "Starting health HAL.";
262   }
263 
264   auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, binder);
265   return hal_health_loop->StartLoop();
266 }
267