| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/forensics/utils/utc_time_provider.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <cstdlib> |
| #include <optional> |
| |
| #include "src/developer/forensics/utils/time.h" |
| #include "src/lib/files/file.h" |
| |
| namespace forensics { |
| |
| UtcTimeProvider::UtcTimeProvider(std::shared_ptr<sys::ServiceDirectory> services, |
| timekeeper::Clock* clock) |
| : UtcTimeProvider(services, clock, std::nullopt) {} |
| |
| UtcTimeProvider::UtcTimeProvider(std::shared_ptr<sys::ServiceDirectory> services, |
| timekeeper::Clock* clock, |
| PreviousBootFile utc_monotonic_difference_file) |
| : UtcTimeProvider(services, clock, std::optional(utc_monotonic_difference_file)) {} |
| |
| UtcTimeProvider::UtcTimeProvider(std::shared_ptr<sys::ServiceDirectory> services, |
| timekeeper::Clock* clock, |
| std::optional<PreviousBootFile> utc_monotonic_difference_file) |
| : services_(services), |
| clock_(clock), |
| utc_(services_->Connect<fuchsia::time::Utc>()), |
| utc_monotonic_difference_file_(std::move(utc_monotonic_difference_file)), |
| previous_boot_utc_monotonic_difference_(std::nullopt) { |
| utc_.set_error_handler([](const zx_status_t status) { |
| FX_PLOGS(ERROR, status) << "Lost connection with fuchsia.time.Utc"; |
| }); |
| |
| WatchForAccurateUtcTime(); |
| |
| if (!utc_monotonic_difference_file_.has_value()) { |
| return; |
| } |
| |
| std::string buf; |
| if (!files::ReadFileToString(utc_monotonic_difference_file_.value().PreviousBootPath(), &buf)) { |
| return; |
| } |
| |
| previous_boot_utc_monotonic_difference_ = |
| zx::duration(strtoll(buf.c_str(), nullptr, /*base*/ 10)); |
| } |
| |
| std::optional<zx::time_utc> UtcTimeProvider::CurrentTime() const { |
| if (!is_utc_time_accurate_) { |
| return std::nullopt; |
| } |
| |
| return CurrentUtcTimeRaw(clock_); |
| } |
| |
| std::optional<zx::duration> UtcTimeProvider::CurrentUtcMonotonicDifference() const { |
| if (!is_utc_time_accurate_) { |
| return std::nullopt; |
| } |
| |
| if (const std::optional<zx::time_utc> current_utc_time = CurrentUtcTimeRaw(clock_); |
| current_utc_time.has_value()) { |
| const zx::duration utc_monotonic_difference(current_utc_time.value().get() - |
| clock_->Now().get()); |
| if (utc_monotonic_difference_file_.has_value()) { |
| // Write the most recent UTC-monotonic difference in case either clock has been adjusted. |
| files::WriteFile(utc_monotonic_difference_file_.value().CurrentBootPath(), |
| std::to_string(utc_monotonic_difference.get())); |
| } |
| return utc_monotonic_difference; |
| } |
| |
| return std::nullopt; |
| } |
| |
| std::optional<zx::duration> UtcTimeProvider::PreviousBootUtcMonotonicDifference() const { |
| return previous_boot_utc_monotonic_difference_; |
| } |
| |
| void UtcTimeProvider::WatchForAccurateUtcTime() { |
| utc_->WatchState([this](const fuchsia::time::UtcState& state) { |
| switch (state.source()) { |
| case fuchsia::time::UtcSource::UNVERIFIED: |
| case fuchsia::time::UtcSource::EXTERNAL: |
| is_utc_time_accurate_ = true; |
| utc_.Unbind(); |
| |
| // Write the current difference between the UTC and monotonic clocks. |
| if (const std::optional<zx::time_utc> current_utc_time = CurrentUtcTimeRaw(clock_); |
| current_utc_time.has_value() && utc_monotonic_difference_file_.has_value()) { |
| const zx::duration utc_monotonic_difference(current_utc_time.value().get() - |
| clock_->Now().get()); |
| files::WriteFile(utc_monotonic_difference_file_.value().CurrentBootPath(), |
| std::to_string(utc_monotonic_difference.get())); |
| } |
| |
| break; |
| case fuchsia::time::UtcSource::BACKSTOP: |
| // fuchsia.time.Utc does not currently distinguish between devices that have an internal |
| // clock and those that do not. So, if a device has an internal clock, it's possbile that |
| // the device's UTC time is be accurate despite |BACKSTOP| being returned, |
| WatchForAccurateUtcTime(); |
| break; |
| } |
| }); |
| } |
| |
| } // namespace forensics |