blob: d9391de2a74530695de2113630c1f446e846b649 [file] [log] [blame]
// 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(&current_time_exploded);
AdjustExplodedTimeValues(&current_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