chromium/chromeos/ash/components/network/hidden_network_handler.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/network/hidden_network_handler.h"

#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/network/managed_network_configuration_handler.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_metadata_store.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "components/device_event_log/device_event_log.h"

namespace ash {
namespace {

constexpr char kRemoveAttemptResultHistogram[] =
    "Network.Ash.WiFi.Hidden.RemovalAttempt.Result";

constexpr base::TimeDelta kDefaultOverrideInterval = base::Minutes(1);
constexpr base::TimeDelta kOneDay = base::Days(1);
constexpr base::TimeDelta kInitialDelay = base::Seconds(30);

void OnRemoveConfigurationSuccess(const std::string guid) {
  base::UmaHistogramBoolean(kRemoveAttemptResultHistogram, true);
  NET_LOG(EVENT) << "Successfully removed wrongly hidden network: " << guid;
}

void OnRemoveConfigurationFailure(const std::string guid,
                                  const std::string& error_name) {
  base::UmaHistogramBoolean(kRemoveAttemptResultHistogram, false);
  NET_LOG(EVENT) << "Failed to remove wrongly hidden network: " << guid
                 << ", error: " << error_name;
}

base::TimeDelta ComputeMigrationInterval() {
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();

  if (!command_line->HasSwitch(switches::kHiddenNetworkMigrationInterval)) {
    return kOneDay;
  }

  int interval_in_seconds = -1;
  const std::string ascii = command_line->GetSwitchValueASCII(
      switches::kHiddenNetworkMigrationInterval);

  if (ascii.empty() || !base::StringToInt(ascii, &interval_in_seconds) ||
      interval_in_seconds < 1) {
    return kDefaultOverrideInterval;
  }
  return base::Seconds(interval_in_seconds);
}

}  // namespace

void HiddenNetworkHandler::Init(
    ManagedNetworkConfigurationHandler* managed_network_configuration_handler,
    NetworkStateHandler* network_state_handler) {
  DCHECK(NetworkHandler::IsInitialized());
  network_state_handler_ = network_state_handler;
  managed_network_configuration_handler_ =
      managed_network_configuration_handler;
}

void HiddenNetworkHandler::SetNetworkMetadataStore(
    NetworkMetadataStore* network_metadata_store) {
  if (network_metadata_store_) {
    initial_delay_timer_.Stop();
    daily_event_timer_.Stop();
  }

  network_metadata_store_ = network_metadata_store;
  if (!network_metadata_store_) {
    return;
  }

  initial_delay_timer_.Start(
      FROM_HERE, kInitialDelay,
      base::BindOnce(&HiddenNetworkHandler::CleanHiddenNetworks,
                     base::Unretained(this)));
  daily_event_timer_.Start(
      FROM_HERE, ComputeMigrationInterval(),
      base::BindRepeating(&HiddenNetworkHandler::CleanHiddenNetworks,
                          base::Unretained(this)));
}

void HiddenNetworkHandler::CleanHiddenNetworks() {
  NetworkStateHandler::NetworkStateList state_list;
  network_state_handler_->GetNetworkListByType(NetworkTypePattern::WiFi(),
                                               /*configured_only=*/true,
                                               /*visible_only=*/false,
                                               /*limit=*/0, &state_list);
  size_t remove_network_attempts = 0;

  for (const NetworkState* state : state_list) {
    if (!state->hidden_ssid() || state->IsManagedByPolicy()) {
      continue;
    }

    // The last connected timestamp for a network will be zero if the network
    // has never been connected to.
    if (!network_metadata_store_->GetLastConnectedTimestamp(state->guid())
             .is_zero()) {
      continue;
    }

    // The WiFi timestamp will return UnixEpoch() if the network has
    // existed for more than two weeks.
    if (network_metadata_store_->UpdateAndRetrieveWiFiTimestamp(
            state->guid()) != base::Time::UnixEpoch()) {
      continue;
    }

    NET_LOG(EVENT) << "Attempting to remove network configuration with GUID: "
                   << state->guid();

    managed_network_configuration_handler_->RemoveConfiguration(
        state->path(),
        base::BindOnce(&OnRemoveConfigurationSuccess, state->guid()),
        base::BindOnce(&OnRemoveConfigurationFailure, state->guid()));

    remove_network_attempts++;
  }

  base::UmaHistogramCounts100("Network.Ash.WiFi.Hidden.RemovalAttempt",
                              remove_network_attempts);
}

}  // namespace ash