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
31 #include <android-base/file.h>
32 #include <android-base/logging.h>
33 #include <android-base/unique_fd.h>
34 #include <android/hidl/manager/1.0/IServiceManager.h>
35 #include <batteryservice/BatteryServiceConstants.h>
36 #include <cutils/properties.h>
37 #include <healthhalutils/HealthHalUtils.h>
38 #include <hidl/HidlTransportSupport.h>
39 #include <hwbinder/IPCThreadState.h>
40 #include <log/log.h>
41
42 #include <storaged.h>
43 #include <storaged_utils.h>
44
45 using namespace android::base;
46 using namespace chrono;
47 using namespace google::protobuf::io;
48 using namespace storaged_proto;
49
50 namespace {
51
52 /*
53 * The system user is the initial user that is implicitly created on first boot
54 * and hosts most of the system services. Keep this in sync with
55 * frameworks/base/core/java/android/os/UserManager.java
56 */
57 constexpr int USER_SYSTEM = 0;
58
59 constexpr ssize_t benchmark_unit_size = 16 * 1024; // 16KB
60
61 constexpr ssize_t min_benchmark_size = 128 * 1024; // 128KB
62
63 } // namespace
64
65 const uint32_t storaged_t::current_version = 4;
66
67 using android::hardware::interfacesEqual;
68 using android::hardware::Return;
69 using android::hardware::health::V1_0::BatteryStatus;
70 using android::hardware::health::V1_0::toString;
71 using android::hardware::health::V2_0::get_health_service;
72 using android::hardware::health::V2_0::HealthInfo;
73 using android::hardware::health::V2_0::IHealth;
74 using android::hardware::health::V2_0::Result;
75 using android::hidl::manager::V1_0::IServiceManager;
76
77
is_charger_on(BatteryStatus prop)78 inline charger_stat_t is_charger_on(BatteryStatus prop) {
79 return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
80 CHARGER_ON : CHARGER_OFF;
81 }
82
healthInfoChanged(const HealthInfo & props)83 Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
84 mUidm.set_charger_state(is_charger_on(props.legacy.batteryStatus));
85 return android::hardware::Void();
86 }
87
init()88 void storaged_t::init() {
89 init_health_service();
90 mDsm = std::make_unique<disk_stats_monitor>(health);
91 storage_info.reset(storage_info_t::get_storage_info(health));
92 }
93
init_health_service()94 void storaged_t::init_health_service() {
95 if (!mUidm.enabled())
96 return;
97
98 health = get_health_service();
99 if (health == NULL) {
100 LOG_TO(SYSTEM, WARNING) << "health: failed to find IHealth service";
101 return;
102 }
103
104 BatteryStatus status = BatteryStatus::UNKNOWN;
105 auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
106 if (r != Result::SUCCESS) {
107 LOG_TO(SYSTEM, WARNING)
108 << "health: cannot get battery status " << toString(r);
109 return;
110 }
111 if (v == BatteryStatus::UNKNOWN) {
112 LOG_TO(SYSTEM, WARNING) << "health: invalid battery status";
113 }
114 status = v;
115 });
116 if (!ret.isOk()) {
117 LOG_TO(SYSTEM, WARNING) << "health: get charge status transaction error "
118 << ret.description();
119 }
120
121 mUidm.init(is_charger_on(status));
122 // register listener after init uid_monitor
123 health->registerCallback(this);
124 health->linkToDeath(this, 0 /* cookie */);
125 }
126
serviceDied(uint64_t cookie,const wp<::android::hidl::base::V1_0::IBase> & who)127 void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
128 if (health != NULL && interfacesEqual(health, who.promote())) {
129 LOG_TO(SYSTEM, ERROR) << "health service died, exiting";
130 android::hardware::IPCThreadState::self()->stopProcess();
131 exit(1);
132 } else {
133 LOG_TO(SYSTEM, ERROR) << "unknown service died";
134 }
135 }
136
report_storage_info()137 void storaged_t::report_storage_info() {
138 storage_info->report();
139 }
140
141 /* storaged_t */
storaged_t(void)142 storaged_t::storaged_t(void) {
143 mConfig.periodic_chores_interval_unit =
144 property_get_int32("ro.storaged.event.interval",
145 DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
146
147 mConfig.event_time_check_usec =
148 property_get_int32("ro.storaged.event.perf_check", 0);
149
150 mConfig.periodic_chores_interval_disk_stats_publish =
151 property_get_int32("ro.storaged.disk_stats_pub",
152 DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
153
154 mConfig.periodic_chores_interval_uid_io =
155 property_get_int32("ro.storaged.uid_io.interval",
156 DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
157
158 mConfig.periodic_chores_interval_flush_proto =
159 property_get_int32("ro.storaged.flush_proto.interval",
160 DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO);
161
162 mStarttime = time(NULL);
163 mTimer = 0;
164 }
165
add_user_ce(userid_t user_id)166 void storaged_t::add_user_ce(userid_t user_id) {
167 load_proto(user_id);
168 proto_loaded[user_id] = true;
169 }
170
remove_user_ce(userid_t user_id)171 void storaged_t::remove_user_ce(userid_t user_id) {
172 proto_loaded[user_id] = false;
173 mUidm.clear_user_history(user_id);
174 RemoveFileIfExists(proto_path(user_id), nullptr);
175 }
176
load_proto(userid_t user_id)177 void storaged_t::load_proto(userid_t user_id) {
178 string proto_file = proto_path(user_id);
179 ifstream in(proto_file, ofstream::in | ofstream::binary);
180
181 if (!in.good()) return;
182
183 stringstream ss;
184 ss << in.rdbuf();
185 StoragedProto proto;
186 proto.ParseFromString(ss.str());
187
188 const UidIOUsage& uid_io_usage = proto.uid_io_usage();
189 uint32_t computed_crc = crc32(current_version,
190 reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
191 uid_io_usage.ByteSize());
192 if (proto.crc() != computed_crc) {
193 LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file;
194 return;
195 }
196
197 mUidm.load_uid_io_proto(user_id, proto.uid_io_usage());
198
199 if (user_id == USER_SYSTEM) {
200 storage_info->load_perf_history_proto(proto.perf_history());
201 }
202 }
203
prepare_proto(userid_t user_id,StoragedProto * proto)204 char* storaged_t:: prepare_proto(userid_t user_id, StoragedProto* proto) {
205 proto->set_version(current_version);
206
207 const UidIOUsage& uid_io_usage = proto->uid_io_usage();
208 proto->set_crc(crc32(current_version,
209 reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
210 uid_io_usage.ByteSize()));
211
212 uint32_t pagesize = sysconf(_SC_PAGESIZE);
213 if (user_id == USER_SYSTEM) {
214 proto->set_padding("", 1);
215 vector<char> padding;
216 ssize_t size = ROUND_UP(MAX(min_benchmark_size, proto->ByteSize()),
217 pagesize);
218 padding = vector<char>(size - proto->ByteSize(), 0xFD);
219 proto->set_padding(padding.data(), padding.size());
220 while (!IS_ALIGNED(proto->ByteSize(), pagesize)) {
221 padding.push_back(0xFD);
222 proto->set_padding(padding.data(), padding.size());
223 }
224 }
225
226 char* data = nullptr;
227 if (posix_memalign(reinterpret_cast<void**>(&data),
228 pagesize, proto->ByteSize())) {
229 PLOG_TO(SYSTEM, ERROR) << "Faied to alloc aligned buffer (size: "
230 << proto->ByteSize() << ")";
231 return data;
232 }
233
234 proto->SerializeToArray(data, proto->ByteSize());
235 return data;
236 }
237
flush_proto_data(userid_t user_id,const char * data,ssize_t size)238 void storaged_t::flush_proto_data(userid_t user_id,
239 const char* data, ssize_t size) {
240 string proto_file = proto_path(user_id);
241 string tmp_file = proto_file + "_tmp";
242 unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(),
243 O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC |
244 (user_id == USER_SYSTEM ? O_DIRECT : 0),
245 S_IRUSR | S_IWUSR)));
246 if (fd == -1) {
247 PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
248 return;
249 }
250
251 if (user_id == USER_SYSTEM) {
252 time_point<steady_clock> start, end;
253 uint32_t benchmark_size = 0;
254 uint64_t benchmark_time_ns = 0;
255 ssize_t ret;
256 bool first_write = true;
257
258 while (size > 0) {
259 start = steady_clock::now();
260 ret = write(fd, data, MIN(benchmark_unit_size, size));
261 if (ret <= 0) {
262 PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
263 return;
264 }
265 end = steady_clock::now();
266 /*
267 * compute bandwidth after the first write and if write returns
268 * exactly unit size.
269 */
270 if (!first_write && ret == benchmark_unit_size) {
271 benchmark_size += benchmark_unit_size;
272 benchmark_time_ns += duration_cast<nanoseconds>(end - start).count();
273 }
274 size -= ret;
275 data += ret;
276 first_write = false;
277 }
278
279 if (benchmark_size) {
280 int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
281 storage_info->update_perf_history(perf, system_clock::now());
282 }
283 } else {
284 if (!WriteFully(fd, data, size)) {
285 PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
286 return;
287 }
288 }
289
290 fd.reset(-1);
291 rename(tmp_file.c_str(), proto_file.c_str());
292 }
293
flush_proto(userid_t user_id,StoragedProto * proto)294 void storaged_t::flush_proto(userid_t user_id, StoragedProto* proto) {
295 unique_ptr<char> proto_data(prepare_proto(user_id, proto));
296 if (proto_data == nullptr) return;
297
298 flush_proto_data(user_id, proto_data.get(), proto->ByteSize());
299 }
300
flush_protos(unordered_map<int,StoragedProto> * protos)301 void storaged_t::flush_protos(unordered_map<int, StoragedProto>* protos) {
302 for (auto& it : *protos) {
303 /*
304 * Don't flush proto if we haven't attempted to load it from file.
305 */
306 if (proto_loaded[it.first]) {
307 flush_proto(it.first, &it.second);
308 }
309 }
310 }
311
event(void)312 void storaged_t::event(void) {
313 unordered_map<int, StoragedProto> protos;
314
315 if (mDsm->enabled()) {
316 mDsm->update();
317 if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) {
318 mDsm->publish();
319 }
320 }
321
322 if (!(mTimer % mConfig.periodic_chores_interval_uid_io)) {
323 mUidm.report(&protos);
324 }
325
326 if (storage_info) {
327 storage_info->refresh(protos[USER_SYSTEM].mutable_perf_history());
328 }
329
330 if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) {
331 flush_protos(&protos);
332 }
333
334 mTimer += mConfig.periodic_chores_interval_unit;
335 }
336
event_checked(void)337 void storaged_t::event_checked(void) {
338 struct timespec start_ts, end_ts;
339 bool check_time = true;
340
341 if (mConfig.event_time_check_usec &&
342 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
343 check_time = false;
344 PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
345 }
346
347 event();
348
349 if (mConfig.event_time_check_usec && check_time) {
350 if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
351 PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
352 return;
353 }
354 int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
355 (end_ts.tv_nsec - start_ts.tv_nsec) / USEC_TO_NSEC;
356 if (cost > mConfig.event_time_check_usec) {
357 LOG_TO(SYSTEM, ERROR)
358 << "event loop spent " << cost << " usec, threshold "
359 << mConfig.event_time_check_usec << " usec";
360 }
361 }
362 }
363