1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112

content / public / test / network_connection_change_simulator.cc [blame]

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

#include "content/public/test/network_connection_change_simulator.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "build/chromeos_buildflags.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/network_service_util.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/network_change_notifier.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/network_service_test.mojom.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "net/base/network_change_notifier_passive.h"
#endif

namespace content {

// SetConnectionType will block until the network connection changes, and
// unblocking it involves posting a task (see
// NetworkConnectionTracker::OnNetworkChanged). If SetConnectionType is ever
// called downstream of a task run within another RunLoop::Run call, this
// class's RunLoop::Run will deadlock because the task needed to unblock it
// won't be run. To stop this, this class uses RunLoops that allow nested tasks.
constexpr base::RunLoop::Type kRunLoopType =
    base::RunLoop::Type::kNestableTasksAllowed;

NetworkConnectionChangeSimulator::NetworkConnectionChangeSimulator() = default;
NetworkConnectionChangeSimulator::~NetworkConnectionChangeSimulator() = default;

#if BUILDFLAG(IS_CHROMEOS)
void NetworkConnectionChangeSimulator::InitializeChromeosConnectionType() {
  // Manually set the connection type since ChromeOS's NetworkChangeNotifier
  // implementation relies on some other class controlling it (normally
  // NetworkChangeManagerClient), which isn't used on content/.
  net::NetworkChangeNotifierPassive* network_change_notifier =
      static_cast<net::NetworkChangeNotifierPassive*>(
          content::GetNetworkChangeNotifier());
  network_change_notifier->OnConnectionChanged(
      net::NetworkChangeNotifier::CONNECTION_ETHERNET);
  // If the network service is enabled, set the connection type for its
  // NetworkChangeNotifier instance as well.
  if (IsOutOfProcessNetworkService()) {
    mojo::Remote<network::mojom::NetworkChangeManager> manager;
    GetNetworkService()->GetNetworkChangeManager(
        manager.BindNewPipeAndPassReceiver());
    manager->OnNetworkChanged(
        /*dns_changed=*/false, /*ip_address_changed=*/false,
        /*connection_type_changed=*/true,
        network::mojom::ConnectionType::CONNECTION_ETHERNET,
        /*connection_subtype_changed=*/false,
        network::mojom::ConnectionSubtype::SUBTYPE_UNKNOWN);
  }
}
#endif

void NetworkConnectionChangeSimulator::SetConnectionType(
    network::mojom::ConnectionType type) {
  network::NetworkConnectionTracker* network_connection_tracker =
      content::GetNetworkConnectionTracker();
  network::mojom::ConnectionType connection_type =
      network::mojom::ConnectionType::CONNECTION_UNKNOWN;
  run_loop_ = std::make_unique<base::RunLoop>(kRunLoopType);
  network_connection_tracker->AddNetworkConnectionObserver(this);
  SimulateNetworkChange(type);
  // Make sure the underlying network connection type becomes |type|.
  // The while loop is necessary because in some machine such as "Builder
  // linux64 trunk", the |connection_type| can be CONNECTION_ETHERNET before
  // it changes to |type|. So here it needs to wait until the
  // |connection_type| becomes |type|.
  while (
      !network_connection_tracker->GetConnectionType(
          &connection_type,
          base::BindOnce(&NetworkConnectionChangeSimulator::OnConnectionChanged,
                         base::Unretained(this))) ||
      connection_type != type) {
    SimulateNetworkChange(type);
    run_loop_->Run();
    run_loop_ = std::make_unique<base::RunLoop>();
  }
  network_connection_tracker->RemoveNetworkConnectionObserver(this);
}

// static
void NetworkConnectionChangeSimulator::SimulateNetworkChange(
    network::mojom::ConnectionType type) {
  if (IsOutOfProcessNetworkService()) {
    mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
    content::GetNetworkService()->BindTestInterfaceForTesting(
        network_service_test.BindNewPipeAndPassReceiver());
    base::RunLoop run_loop(kRunLoopType);
    network_service_test->SimulateNetworkChange(type, run_loop.QuitClosure());
    run_loop.Run();
    return;
  }
  net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
      net::NetworkChangeNotifier::ConnectionType(type));
}

void NetworkConnectionChangeSimulator::OnConnectionChanged(
    network::mojom::ConnectionType connection_type) {
  DCHECK(run_loop_);
  run_loop_->Quit();
}

}  // namespace content