| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "Storage.h" |
| |
| #include <sstream> |
| |
| #include <android-base/chrono_utils.h> |
| #include <android-base/file.h> |
| #include <android-base/logging.h> |
| #include <android-base/strings.h> |
| #include <fstab/fstab.h> |
| |
| namespace android { |
| namespace hardware { |
| namespace health { |
| namespace storage { |
| namespace V1_0 { |
| namespace implementation { |
| |
| using base::ReadFileToString; |
| using base::Timer; |
| using base::Trim; |
| using base::WriteStringToFd; |
| using base::WriteStringToFile; |
| using fs_mgr::Fstab; |
| using fs_mgr::ReadDefaultFstab; |
| |
| std::string getGarbageCollectPath() { |
| Fstab fstab; |
| ReadDefaultFstab(&fstab); |
| |
| for (const auto& entry : fstab) { |
| if (!entry.sysfs_path.empty()) { |
| return entry.sysfs_path + "/manual_gc"; |
| } |
| } |
| |
| return ""; |
| } |
| |
| Return<void> Storage::garbageCollect(uint64_t timeoutSeconds, |
| const sp<IGarbageCollectCallback>& cb) { |
| Result result = Result::SUCCESS; |
| std::string path = getGarbageCollectPath(); |
| |
| if (path.empty()) { |
| LOG(WARNING) << "Cannot find Dev GC path"; |
| result = Result::UNKNOWN_ERROR; |
| } else { |
| Timer timer; |
| LOG(INFO) << "Start Dev GC on " << path; |
| while (1) { |
| std::string require; |
| if (!ReadFileToString(path, &require)) { |
| PLOG(WARNING) << "Reading manual_gc failed in " << path; |
| result = Result::IO_ERROR; |
| break; |
| } |
| require = Trim(require); |
| if (require == "" || require == "off" || require == "disabled") { |
| LOG(DEBUG) << "No more to do Dev GC"; |
| break; |
| } |
| LOG(DEBUG) << "Trigger Dev GC on " << path; |
| if (!WriteStringToFile("1", path)) { |
| PLOG(WARNING) << "Start Dev GC failed on " << path; |
| result = Result::IO_ERROR; |
| break; |
| } |
| if (timer.duration() >= std::chrono::seconds(timeoutSeconds)) { |
| LOG(WARNING) << "Dev GC timeout"; |
| // Timeout is not treated as an error. Try next time. |
| break; |
| } |
| sleep(2); |
| } |
| LOG(INFO) << "Stop Dev GC on " << path; |
| if (!WriteStringToFile("0", path)) { |
| PLOG(WARNING) << "Stop Dev GC failed on " << path; |
| result = Result::IO_ERROR; |
| } |
| } |
| |
| if (cb != nullptr) { |
| auto ret = cb->onFinish(result); |
| if (!ret.isOk()) { |
| LOG(WARNING) << "Cannot return result to callback: " << ret.description(); |
| } |
| } |
| return Void(); |
| } |
| |
| Return<void> Storage::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) { |
| if (handle == nullptr || handle->numFds < 1) { |
| return Void(); |
| } |
| |
| int fd = handle->data[0]; |
| std::stringstream output; |
| |
| std::string path = getGarbageCollectPath(); |
| if (path.empty()) { |
| output << "Cannot find Dev GC path"; |
| } else { |
| std::string require; |
| |
| if (ReadFileToString(path, &require)) { |
| output << path << ":" << require << std::endl; |
| } |
| |
| if (WriteStringToFile("0", path)) { |
| output << "stop success" << std::endl; |
| } |
| } |
| |
| if (!WriteStringToFd(output.str(), fd)) { |
| PLOG(WARNING) << "debug: cannot write to fd"; |
| } |
| |
| fsync(fd); |
| |
| return Void(); |
| } |
| |
| } // namespace implementation |
| } // namespace V1_0 |
| } // namespace storage |
| } // namespace health |
| } // namespace hardware |
| } // namespace android |