• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 "storaged"
18 
19 #include <dirent.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <time.h>
23 #include <unistd.h>
24 #include <zlib.h>
25 
26 #include <chrono>
27 #include <fstream>
28 #include <sstream>
29 #include <string>
30 #include <utility>
31 
32 #include <aidl/android/hardware/health/BnHealthInfoCallback.h>
33 #include <android-base/file.h>
34 #include <android-base/logging.h>
35 #include <android-base/unique_fd.h>
36 #include <android/binder_ibinder.h>
37 #include <android/binder_manager.h>
38 #include <android/hidl/manager/1.0/IServiceManager.h>
39 #include <batteryservice/BatteryServiceConstants.h>
40 #include <cutils/properties.h>
41 #include <health-shim/shim.h>
42 #include <healthhalutils/HealthHalUtils.h>
43 #include <hidl/HidlTransportSupport.h>
44 #include <hwbinder/IPCThreadState.h>
45 #include <log/log.h>
46 
47 #include <storaged.h>
48 #include <storaged_utils.h>
49 
50 using namespace android::base;
51 using namespace chrono;
52 using namespace google::protobuf::io;
53 using namespace storaged_proto;
54 
55 namespace {
56 
57 /*
58  * The system user is the initial user that is implicitly created on first boot
59  * and hosts most of the system services. Keep this in sync with
60  * frameworks/base/core/java/android/os/UserManager.java
61  */
62 constexpr int USER_SYSTEM = 0;
63 
64 constexpr ssize_t benchmark_unit_size = 16 * 1024;  // 16KB
65 
66 constexpr size_t min_benchmark_size = 128 * 1024;  // 128KB
67 
68 }  // namespace
69 
70 const uint32_t storaged_t::current_version = 4;
71 
72 using aidl::android::hardware::health::BatteryStatus;
73 using aidl::android::hardware::health::BnHealthInfoCallback;
74 using aidl::android::hardware::health::HealthInfo;
75 using aidl::android::hardware::health::IHealth;
76 using aidl::android::hardware::health::IHealthInfoCallback;
77 using android::hardware::interfacesEqual;
78 using android::hardware::health::V2_0::get_health_service;
79 using android::hidl::manager::V1_0::IServiceManager;
80 using HidlHealth = android::hardware::health::V2_0::IHealth;
81 using aidl::android::hardware::health::HealthShim;
82 using ndk::ScopedAIBinder_DeathRecipient;
83 using ndk::ScopedAStatus;
84 
get()85 HealthServicePair HealthServicePair::get() {
86     HealthServicePair ret;
87     auto service_name = IHealth::descriptor + "/default"s;
88     if (AServiceManager_isDeclared(service_name.c_str())) {
89         ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str()));
90         ret.aidl_health = IHealth::fromBinder(binder);
91         if (ret.aidl_health == nullptr) {
92             LOG(WARNING) << "AIDL health service is declared, but it cannot be retrieved.";
93         }
94     }
95     if (ret.aidl_health == nullptr) {
96         LOG(INFO) << "Unable to get AIDL health service, trying HIDL...";
97         ret.hidl_health = get_health_service();
98         if (ret.hidl_health != nullptr) {
99             ret.aidl_health = ndk::SharedRefBase::make<HealthShim>(ret.hidl_health);
100         }
101     }
102     if (ret.aidl_health == nullptr) {
103         LOG(WARNING) << "health: failed to find IHealth service";
104         return {};
105     }
106     return ret;
107 }
108 
is_charger_on(BatteryStatus prop)109 inline charger_stat_t is_charger_on(BatteryStatus prop) {
110     return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
111         CHARGER_ON : CHARGER_OFF;
112 }
113 
114 class HealthInfoCallback : public BnHealthInfoCallback {
115   public:
HealthInfoCallback(uid_monitor * uidm)116     HealthInfoCallback(uid_monitor* uidm) : mUidm(uidm) {}
healthInfoChanged(const HealthInfo & info)117     ScopedAStatus healthInfoChanged(const HealthInfo& info) override {
118         mUidm->set_charger_state(is_charger_on(info.batteryStatus));
119         return ScopedAStatus::ok();
120     }
121 
122   private:
123     uid_monitor* mUidm;
124 };
125 
init()126 void storaged_t::init() {
127     init_health_service();
128     mDsm = std::make_unique<disk_stats_monitor>(health);
129     storage_info.reset(storage_info_t::get_storage_info(health));
130 }
131 
onHealthBinderDied(void *)132 static void onHealthBinderDied(void*) {
133     LOG(ERROR) << "health service died, exiting";
134     android::hardware::IPCThreadState::self()->stopProcess();
135     exit(1);
136 }
137 
init_health_service()138 void storaged_t::init_health_service() {
139     if (!mUidm.enabled())
140         return;
141 
142     auto [aidlHealth, hidlHealth] = HealthServicePair::get();
143     health = aidlHealth;
144     if (health == nullptr) return;
145 
146     BatteryStatus status = BatteryStatus::UNKNOWN;
147     auto ret = health->getChargeStatus(&status);
148     if (!ret.isOk()) {
149         LOG(WARNING) << "health: cannot get battery status: " << ret.getDescription();
150     }
151     if (status == BatteryStatus::UNKNOWN) {
152         LOG(WARNING) << "health: invalid battery status";
153     }
154 
155     mUidm.init(is_charger_on(status));
156     // register listener after init uid_monitor
157     aidl_health_callback = ndk::SharedRefBase::make<HealthInfoCallback>(&mUidm);
158     ret = health->registerCallback(aidl_health_callback);
159     if (!ret.isOk()) {
160         LOG(WARNING) << "health: failed to register callback: " << ret.getDescription();
161     }
162 
163     if (hidlHealth != nullptr) {
164         hidl_death_recp = new hidl_health_death_recipient(hidlHealth);
165         auto ret = hidlHealth->linkToDeath(hidl_death_recp, 0 /* cookie */);
166         if (!ret.isOk()) {
167             LOG(WARNING) << "Failed to link to death (HIDL): " << ret.description();
168         }
169     } else {
170         aidl_death_recp =
171                 ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(onHealthBinderDied));
172         auto ret = AIBinder_linkToDeath(health->asBinder().get(), aidl_death_recp.get(),
173                                         nullptr /* cookie */);
174         if (ret != STATUS_OK) {
175             LOG(WARNING) << "Failed to link to death (AIDL): "
176                          << ScopedAStatus(AStatus_fromStatus(ret)).getDescription();
177         }
178     }
179 }
180 
serviceDied(uint64_t cookie,const wp<::android::hidl::base::V1_0::IBase> & who)181 void hidl_health_death_recipient::serviceDied(uint64_t cookie,
182                                               const wp<::android::hidl::base::V1_0::IBase>& who) {
183     if (mHealth != nullptr && interfacesEqual(mHealth, who.promote())) {
184         onHealthBinderDied(reinterpret_cast<void*>(cookie));
185     } else {
186         LOG(ERROR) << "unknown service died";
187     }
188 }
189 
report_storage_info()190 void storaged_t::report_storage_info() {
191     storage_info->report();
192 }
193 
194 /* storaged_t */
storaged_t(void)195 storaged_t::storaged_t(void) {
196     mConfig.periodic_chores_interval_unit =
197         property_get_int32("ro.storaged.event.interval",
198                            DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
199 
200     mConfig.event_time_check_usec =
201         property_get_int32("ro.storaged.event.perf_check", 0);
202 
203     mConfig.periodic_chores_interval_disk_stats_publish =
204         property_get_int32("ro.storaged.disk_stats_pub",
205                            DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
206 
207     mConfig.periodic_chores_interval_uid_io =
208         property_get_int32("ro.storaged.uid_io.interval",
209                            DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
210 
211     mConfig.periodic_chores_interval_flush_proto =
212         property_get_int32("ro.storaged.flush_proto.interval",
213                            DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO);
214 
215     mStarttime = time(NULL);
216     mTimer = 0;
217 }
218 
add_user_ce(userid_t user_id)219 void storaged_t::add_user_ce(userid_t user_id) {
220     Mutex::Autolock _l(proto_lock);
221 
222     if (!proto_loaded[user_id]) {
223         load_proto(user_id);
224         proto_loaded[user_id] = true;
225     }
226 }
227 
remove_user_ce(userid_t user_id)228 void storaged_t::remove_user_ce(userid_t user_id) {
229     Mutex::Autolock _l(proto_lock);
230 
231     proto_loaded[user_id] = false;
232     mUidm.clear_user_history(user_id);
233     RemoveFileIfExists(proto_path(user_id), nullptr);
234 }
235 
load_proto(userid_t user_id)236 void storaged_t::load_proto(userid_t user_id) {
237     string proto_file = proto_path(user_id);
238     ifstream in(proto_file, ofstream::in | ofstream::binary);
239 
240     if (!in.good()) return;
241 
242     stringstream ss;
243     ss << in.rdbuf();
244     StoragedProto proto;
245     proto.ParseFromString(ss.str());
246 
247     const UidIOUsage& uid_io_usage = proto.uid_io_usage();
248     uint32_t computed_crc =
249             crc32(current_version,
250                   reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
251                   uid_io_usage.ByteSizeLong());
252     if (proto.crc() != computed_crc) {
253         LOG(WARNING) << "CRC mismatch in " << proto_file;
254         return;
255     }
256 
257     mUidm.load_uid_io_proto(user_id, proto.uid_io_usage());
258 
259     if (user_id == USER_SYSTEM) {
260         storage_info->load_perf_history_proto(proto.perf_history());
261     }
262 }
263 
prepare_proto(userid_t user_id,StoragedProto * proto)264 char* storaged_t:: prepare_proto(userid_t user_id, StoragedProto* proto) {
265     proto->set_version(current_version);
266 
267     const UidIOUsage& uid_io_usage = proto->uid_io_usage();
268     proto->set_crc(crc32(current_version,
269                          reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
270                          uid_io_usage.ByteSizeLong()));
271 
272     uint32_t pagesize = sysconf(_SC_PAGESIZE);
273     if (user_id == USER_SYSTEM) {
274         proto->set_padding("", 1);
275         vector<char> padding;
276         ssize_t size = ROUND_UP(std::max(min_benchmark_size, proto->ByteSizeLong()), pagesize);
277         padding = vector<char>(size - proto->ByteSizeLong(), 0xFD);
278         proto->set_padding(padding.data(), padding.size());
279         while (!IS_ALIGNED(proto->ByteSizeLong(), pagesize)) {
280             padding.push_back(0xFD);
281             proto->set_padding(padding.data(), padding.size());
282         }
283     }
284 
285     char* data = nullptr;
286     if (posix_memalign(reinterpret_cast<void**>(&data), pagesize, proto->ByteSizeLong())) {
287         PLOG(ERROR) << "Faied to alloc aligned buffer (size: " << proto->ByteSizeLong() << ")";
288         return data;
289     }
290 
291     proto->SerializeToArray(data, proto->ByteSizeLong());
292     return data;
293 }
294 
flush_proto_data(userid_t user_id,const char * data,ssize_t size)295 void storaged_t::flush_proto_data(userid_t user_id,
296                                   const char* data, ssize_t size) {
297     string proto_file = proto_path(user_id);
298     string tmp_file = proto_file + "_tmp";
299     unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(),
300                  O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC |
301                     (user_id == USER_SYSTEM ? O_DIRECT : 0),
302                  S_IRUSR | S_IWUSR)));
303     if (fd == -1) {
304         PLOG(ERROR) << "Faied to open tmp file: " << tmp_file;
305         return;
306     }
307 
308     if (user_id == USER_SYSTEM) {
309         time_point<steady_clock> start, end;
310         uint32_t benchmark_size = 0;
311         uint64_t benchmark_time_ns = 0;
312         ssize_t ret;
313         bool first_write = true;
314 
315         while (size > 0) {
316             start = steady_clock::now();
317             ret = write(fd, data, std::min(benchmark_unit_size, size));
318             if (ret <= 0) {
319                 PLOG(ERROR) << "Faied to write tmp file: " << tmp_file;
320                 return;
321             }
322             end = steady_clock::now();
323             /*
324             * compute bandwidth after the first write and if write returns
325             * exactly unit size.
326             */
327             if (!first_write && ret == benchmark_unit_size) {
328                 benchmark_size += benchmark_unit_size;
329                 benchmark_time_ns += duration_cast<nanoseconds>(end - start).count();
330             }
331             size -= ret;
332             data += ret;
333             first_write = false;
334         }
335 
336         if (benchmark_size && benchmark_time_ns) {
337             int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
338             storage_info->update_perf_history(perf, system_clock::now());
339         }
340     } else {
341         if (!WriteFully(fd, data, size)) {
342             PLOG(ERROR) << "Faied to write tmp file: " << tmp_file;
343             return;
344         }
345     }
346 
347     fd.reset(-1);
348     rename(tmp_file.c_str(), proto_file.c_str());
349 }
350 
flush_proto(userid_t user_id,StoragedProto * proto)351 void storaged_t::flush_proto(userid_t user_id, StoragedProto* proto) {
352     unique_ptr<char> proto_data(prepare_proto(user_id, proto));
353     if (proto_data == nullptr) return;
354 
355     flush_proto_data(user_id, proto_data.get(), proto->ByteSizeLong());
356 }
357 
flush_protos(unordered_map<int,StoragedProto> * protos)358 void storaged_t::flush_protos(unordered_map<int, StoragedProto>* protos) {
359     Mutex::Autolock _l(proto_lock);
360 
361     for (auto& it : *protos) {
362         /*
363          * Don't flush proto if we haven't attempted to load it from file.
364          */
365         if (proto_loaded[it.first]) {
366             flush_proto(it.first, &it.second);
367         }
368     }
369 }
370 
event(void)371 void storaged_t::event(void) {
372     unordered_map<int, StoragedProto> protos;
373 
374     if (mDsm->enabled()) {
375         mDsm->update();
376         if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) {
377             mDsm->publish();
378         }
379     }
380 
381     if (!(mTimer % mConfig.periodic_chores_interval_uid_io)) {
382         mUidm.report(&protos);
383     }
384 
385     if (storage_info) {
386         storage_info->refresh(protos[USER_SYSTEM].mutable_perf_history());
387     }
388 
389     if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) {
390         flush_protos(&protos);
391     }
392 
393     mTimer += mConfig.periodic_chores_interval_unit;
394 }
395 
event_checked(void)396 void storaged_t::event_checked(void) {
397     struct timespec start_ts, end_ts;
398     bool check_time = true;
399 
400     if (mConfig.event_time_check_usec &&
401         clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
402         check_time = false;
403         PLOG(ERROR) << "clock_gettime() failed";
404     }
405 
406     event();
407 
408     if (mConfig.event_time_check_usec && check_time) {
409         if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
410             PLOG(ERROR) << "clock_gettime() failed";
411             return;
412         }
413         int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
414                        (end_ts.tv_nsec - start_ts.tv_nsec) / USEC_TO_NSEC;
415         if (cost > mConfig.event_time_check_usec) {
416             LOG(ERROR) << "event loop spent " << cost << " usec, threshold "
417                        << mConfig.event_time_check_usec << " usec";
418         }
419     }
420 }
421