| // Copyright 2021 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "chrome/browser/ash/net/traffic_counters_handler.h" | 
 |  | 
 | #include <memory> | 
 | #include <string> | 
 |  | 
 | #include "ash/public/cpp/network_config_service.h" | 
 | #include "base/time/time.h" | 
 | #include "base/values.h" | 
 | #include "chromeos/ash/components/network/network_event_log.h" | 
 | #include "chromeos/ash/components/network/network_handler.h" | 
 | #include "chromeos/ash/components/network/network_metadata_store.h" | 
 | #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h" | 
 | #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h" | 
 |  | 
 | namespace ash { | 
 |  | 
 | namespace { | 
 |  | 
 | // Interval duration to determine the auto reset check frequency. | 
 | constexpr base::TimeDelta kResetCheckInterval = base::Hours(6); | 
 |  | 
 | base::Time GetValidTime(base::Time::Exploded exploded_time) { | 
 |   base::Time time; | 
 |   while (!base::Time::FromLocalExploded(exploded_time, &time)) { | 
 |     if (exploded_time.day_of_month > 28) | 
 |       --exploded_time.day_of_month; | 
 |     else | 
 |       break; | 
 |   } | 
 |   return time; | 
 | } | 
 |  | 
 | // To avoid discrepancies between different times of the same day, set all times | 
 | // to 12:01:00 AM. This is safe to do so because traffic counters will never be | 
 | // automatically reset more than once on any given day. | 
 | void AdjustExplodedTimeValues(base::Time::Exploded* exploded_time) { | 
 |   exploded_time->hour = 0; | 
 |   exploded_time->minute = 1; | 
 |   exploded_time->second = 0; | 
 |   exploded_time->millisecond = 0; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | namespace traffic_counters { | 
 |  | 
 | TrafficCountersHandler::TrafficCountersHandler() | 
 |     : time_getter_(base::BindRepeating([]() { return base::Time::Now(); })), | 
 |       timer_(std::make_unique<base::RepeatingTimer>()) { | 
 |   GetNetworkConfigService( | 
 |       remote_cros_network_config_.BindNewPipeAndPassReceiver()); | 
 |   remote_cros_network_config_->AddObserver( | 
 |       cros_network_config_observer_receiver_.BindNewPipeAndPassRemote()); | 
 | } | 
 |  | 
 | TrafficCountersHandler::~TrafficCountersHandler() = default; | 
 |  | 
 | void TrafficCountersHandler::Start() { | 
 |   RunAll(); | 
 |   timer_->Start(FROM_HERE, kResetCheckInterval, this, | 
 |                 &TrafficCountersHandler::RunAll); | 
 | } | 
 |  | 
 | void TrafficCountersHandler::RunAll() { | 
 |   RunWithFilter(chromeos::network_config::mojom::FilterType::kAll); | 
 | } | 
 |  | 
 | void TrafficCountersHandler::RunWithFilter( | 
 |     chromeos::network_config::mojom::FilterType filter_type) { | 
 |   NET_LOG(EVENT) << "Starting run with filter type " << filter_type | 
 |                  << " at: " << time_getter_.Run(); | 
 |   DCHECK(remote_cros_network_config_); | 
 |   remote_cros_network_config_->GetNetworkStateList( | 
 |       chromeos::network_config::mojom::NetworkFilter::New( | 
 |           filter_type, chromeos::network_config::mojom::NetworkType::kAll, | 
 |           chromeos::network_config::mojom::kNoLimit), | 
 |       base::BindOnce(&TrafficCountersHandler::OnNetworkStateListReceived, | 
 |                      weak_ptr_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | void TrafficCountersHandler::OnActiveNetworksChanged( | 
 |     std::vector<chromeos::network_config::mojom::NetworkStatePropertiesPtr> | 
 |         active_networks) { | 
 |   RunWithFilter(chromeos::network_config::mojom::FilterType::kActive); | 
 | } | 
 |  | 
 | void TrafficCountersHandler::OnNetworkStateListReceived( | 
 |     std::vector<chromeos::network_config::mojom::NetworkStatePropertiesPtr> | 
 |         networks) { | 
 |   for (const auto& network : networks) { | 
 |     if (!GetAutoResetEnabled(network->guid)) { | 
 |       continue; | 
 |     } | 
 |     remote_cros_network_config_->GetManagedProperties( | 
 |         network->guid, | 
 |         base::BindOnce(&TrafficCountersHandler::OnManagedPropertiesReceived, | 
 |                        weak_ptr_factory_.GetWeakPtr(), network->guid)); | 
 |   } | 
 | } | 
 |  | 
 | bool TrafficCountersHandler::GetAutoResetEnabled(std::string guid) { | 
 |   NetworkMetadataStore* metadata_store = | 
 |       NetworkHandler::Get()->network_metadata_store(); | 
 |   DCHECK(metadata_store); | 
 |   const base::Value* enabled = | 
 |       metadata_store->GetEnableTrafficCountersAutoReset(guid); | 
 |   return enabled && enabled->GetBool(); | 
 | } | 
 |  | 
 | void TrafficCountersHandler::OnManagedPropertiesReceived( | 
 |     std::string guid, | 
 |     chromeos::network_config::mojom::ManagedPropertiesPtr managed_properties) { | 
 |   if (!managed_properties) { | 
 |     NET_LOG(ERROR) << "Failed to retrive properties for: " | 
 |                    << NetworkGuidId(guid); | 
 |     return; | 
 |   } | 
 |   if (!managed_properties->traffic_counter_properties) { | 
 |     NET_LOG(ERROR) << "Failed to retrieve traffic counter properties for: " | 
 |                    << NetworkGuidId(guid); | 
 |   } | 
 |   bool should_reset; | 
 |   if (!managed_properties->traffic_counter_properties->last_reset_time | 
 |            .has_value()) { | 
 |     // No last reset time, trigger an initial reset. | 
 |     should_reset = true; | 
 |   } else { | 
 |     base::Time last_reset_time = base::Time::FromDeltaSinceWindowsEpoch( | 
 |         managed_properties->traffic_counter_properties->last_reset_time | 
 |             ->ToDeltaSinceWindowsEpoch()); | 
 |     should_reset = ShouldReset(guid, last_reset_time); | 
 |   } | 
 |   if (should_reset) { | 
 |     NET_LOG(EVENT) << "Resetting traffic counters for network: " | 
 |                    << NetworkGuidId(guid); | 
 |     remote_cros_network_config_->ResetTrafficCounters(guid); | 
 |   } | 
 | } | 
 |  | 
 | // Note that if a user manually resets the traffic counters on the user | 
 | // specified reset day before TrafficCountersHandler runs, | 
 | // TrafficCountersHandler class will not automatically reset the counters until | 
 | // the reset day the following month. | 
 | bool TrafficCountersHandler::ShouldReset(std::string guid, | 
 |                                          base::Time last_reset_time) { | 
 |   NetworkMetadataStore* metadata_store = | 
 |       NetworkHandler::Get()->network_metadata_store(); | 
 |   DCHECK(metadata_store); | 
 |   const base::Value* reset_day_ptr = | 
 |       metadata_store->GetDayOfTrafficCountersAutoReset(guid); | 
 |   if (!reset_day_ptr) { | 
 |     NET_LOG(ERROR) << "Failed to retrieve auto reset day for network: " | 
 |                    << NetworkGuidId(guid); | 
 |     return false; | 
 |   } | 
 |   auto user_specified_reset_day = reset_day_ptr->GetInt(); | 
 |  | 
 |   base::Time::Exploded current_time_exploded; | 
 |   time_getter_.Run().LocalExplode(¤t_time_exploded); | 
 |   AdjustExplodedTimeValues(¤t_time_exploded); | 
 |  | 
 |   base::Time::Exploded last_reset_time_exploded; | 
 |   last_reset_time.LocalExplode(&last_reset_time_exploded); | 
 |   AdjustExplodedTimeValues(&last_reset_time_exploded); | 
 |   if (!base::Time::FromLocalExploded(last_reset_time_exploded, | 
 |                                      &last_reset_time)) { | 
 |     NET_LOG(ERROR) << "Failed to set last_reset_time to 12:01:00 AM"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool result = false; | 
 |   base::Time expected_last_reset_time = | 
 |       GetExpectedLastResetTime(current_time_exploded, user_specified_reset_day); | 
 |   if (expected_last_reset_time > last_reset_time) { | 
 |     // If the actual last auto reset occurred before our expected last | 
 |     // auto reset time, traffic counters should be reset. | 
 |     result = true; | 
 |   } | 
 |  | 
 |   VLOG(3) << "ShouldReset for: " << guid << " at: " << time_getter_.Run() | 
 |           << " last: " << last_reset_time | 
 |           << " expected_last: " << expected_last_reset_time | 
 |           << " day: " << user_specified_reset_day << " = " << result; | 
 |  | 
 |   // expected_last_reset_time.ToDeltaSinceWindowsEpoch() <= | 
 |   // actual_last_reset_time.ToDeltaSinceWindowsEpoch(). Don't reset traffic | 
 |   // counters. | 
 |   return result; | 
 | } | 
 |  | 
 | base::Time TrafficCountersHandler::GetExpectedLastResetTime( | 
 |     const base::Time::Exploded& current_time_exploded, | 
 |     int user_specified_reset_day) { | 
 |   base::Time::Exploded exploded = current_time_exploded; | 
 |   exploded.day_of_month = user_specified_reset_day; | 
 |   GetValidTime(exploded).LocalExplode(&exploded); | 
 |  | 
 |   // If the user specified reset day is greater than the current day, then the | 
 |   // expected last reset day is on the user specified day of the previous | 
 |   // month. Concretely, if e.g., user_specified_reset_day = 14 and current day | 
 |   // = 13, the last reset day is expected to be on the 14th of the previous | 
 |   // month. Otherwise, we expect that the last reset occurred in the current | 
 |   // month. | 
 |   if (exploded.day_of_month > current_time_exploded.day_of_month) { | 
 |     if (--exploded.month < 1) { | 
 |       exploded.month = 12; | 
 |       exploded.year--; | 
 |     } | 
 |   } | 
 |   return GetValidTime(exploded); | 
 | } | 
 |  | 
 | void TrafficCountersHandler::RunForTesting() { | 
 |   RunWithFilter(chromeos::network_config::mojom::FilterType::kAll); | 
 | } | 
 |  | 
 | void TrafficCountersHandler::SetTimeGetterForTest(TimeGetter time_getter) { | 
 |   time_getter_ = std::move(time_getter); | 
 | } | 
 |  | 
 | }  // namespace traffic_counters | 
 |  | 
 | }  // namespace ash |