blob: 1890f2981df8100cbb608135c422ad050e9b71b3 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_REGISTRY_H_
#define CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_REGISTRY_H_
#include <stddef.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/timer/timer.h"
#include "chrome/browser/media/router/discovery/dial/dial_service.h"
#include "services/network/public/cpp/network_connection_tracker.h"
#include "url/gurl.h"
namespace base {
class Clock;
}
namespace net {
class NetLog;
}
namespace media_router {
// Keeps track of devices that have responded to discovery requests and notifies
// the client with the current device list. All APIs should be called on the
// sequence bound to |task_runner_|.
class DialRegistry
: public DialService::Client,
public network::NetworkConnectionTracker::NetworkConnectionObserver {
public:
using DeviceList = std::vector<DialDeviceData>;
enum DialErrorCode {
DIAL_NO_LISTENERS = 0, // Deprecated
DIAL_NO_INTERFACES,
DIAL_NETWORK_DISCONNECTED,
DIAL_CELLULAR_NETWORK,
DIAL_SOCKET_ERROR,
DIAL_UNKNOWN
};
class Client {
public:
// Called when the list of DIAL devices has changed. Will be called
// multiple times.
virtual void OnDialDeviceList(const DeviceList& devices) = 0;
// Called when an error has occurred.
virtual void OnDialError(DialErrorCode type) = 0;
protected:
virtual ~Client() = default;
};
DialRegistry(DialRegistry::Client& client,
const scoped_refptr<base::SequencedTaskRunner>& task_runner);
DialRegistry(const DialRegistry&) = delete;
DialRegistry(DialRegistry&&) = delete;
DialRegistry& operator=(const DialRegistry&) = delete;
DialRegistry& operator=(DialRegistry&&) = delete;
~DialRegistry() override;
// Sets the NetLog object used for logging. If the registry already has a
// NetLog, does nothing. The NetLog should live at least as long as the IO
// Thread.
void SetNetLog(net::NetLog* net_log);
// Waits for a suitable network interface to be up and then starts periodic
// discovery. Must be called before DiscoverNow() or
// StartPeriodicDiscovery().
void Start();
// Starts a discovery cycle immediately.
bool DiscoverNow();
// Starts and stops periodic background discovery.
void StartPeriodicDiscovery();
void StopPeriodicDiscovery();
// Returns the URL of the device description for the device identified by
// |label|, or an empty GURL if no such device exists.
GURL GetDeviceDescriptionURL(const std::string& label) const;
// Adds a device directly to the registry as if it was discovered. For tests
// only. Note that if discovery is actually started, this device will be
// removed by PruneExpiredDevices().
void AddDeviceForTest(const DialDeviceData& device_data);
// Allows tests to swap in a fake clock.
void SetClockForTest(base::Clock* clock);
protected:
// Returns a new instance of the DIAL service. Overridden by tests.
virtual std::unique_ptr<DialService> CreateDialService();
virtual void ClearDialService();
// The DIAL service. Periodic discovery is active when this is not NULL.
std::unique_ptr<DialService> dial_;
private:
using DeviceByIdMap = std::map<std::string, std::unique_ptr<DialDeviceData>>;
using DeviceByLabelMap = std::map<std::string, DialDeviceData*>;
friend class MockDialRegistry;
friend class TestDialRegistry;
// Called when we've gotten the NetworkConnectionTracker from the UI thread.
void SetNetworkConnectionTracker(network::NetworkConnectionTracker* tracker);
// DialService::Client:
void OnDiscoveryRequest() override;
void OnDeviceDiscovered(const DialDeviceData& device) override;
void OnDiscoveryFinished() override;
void OnError(DialService::DialServiceErrorCode code) override;
// network::NetworkConnectionTracker::NetworkConnectionObserver:
void OnConnectionChanged(network::mojom::ConnectionType type) override;
// Check whether we are in a state ready to discover and dispatch error
// notifications if not.
bool ReadyToDiscover();
// Purge our whole registry. We may need to do this occasionally, e.g. when
// the network status changes. Increments the registry generation.
void Clear();
// The repeating timer schedules discoveries with this method.
void DoDiscovery();
// Attempts to add a newly discovered device to the registry. Returns true if
// successful.
bool MaybeAddDevice(std::unique_ptr<DialDeviceData> device_data);
// Remove devices from the registry that have expired, i.e. not responded
// after some time. Returns true if the registry was modified.
bool PruneExpiredDevices();
// Returns true if the device has expired and should be removed from the
// active set.
bool IsDeviceExpired(const DialDeviceData& device) const;
// Notify the client with the current device list if the list has changed.
void MaybeSendDeviceList();
// Returns the next label to use for a newly-seen device.
std::string NextLabel();
// Unowned reference to the DialRegistry::Client.
Client& client_;
// Task runner for the DialRegistry.
scoped_refptr<base::SequencedTaskRunner> task_runner_;
// Incremented each time we modify the registry of active devices.
int registry_generation_;
// The registry generation associated with the last time we sent an event.
// Used to suppress events with duplicate device lists.
int last_event_registry_generation_;
// Counter to generate device labels.
int label_count_;
// Registry parameters
const base::TimeDelta refresh_interval_delta_;
const base::TimeDelta expiration_delta_;
const size_t max_devices_;
// A map used to track known devices by their device_id.
DeviceByIdMap device_by_id_map_;
// A map used to track known devices sorted by label. We iterate over this to
// construct the device list sent to API clients.
DeviceByLabelMap device_by_label_map_;
// Timer used to manage periodic discovery requests.
std::unique_ptr<base::RepeatingTimer> repeating_timer_;
// Set just after construction.
raw_ptr<net::NetLog> net_log_ = nullptr;
raw_ptr<network::NetworkConnectionTracker> network_connection_tracker_ =
nullptr;
raw_ptr<base::Clock> clock_;
SEQUENCE_CHECKER(sequence_checker_);
friend class DialMediaSinkServiceImplTest;
friend class DialRegistryTest;
FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestAddRemoveListeners);
FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestNoDevicesDiscovered);
FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestDevicesDiscovered);
FRIEND_TEST_ALL_PREFIXES(DialRegistryTest,
TestDevicesDiscoveredWithTwoListeners);
FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestDeviceExpires);
FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestExpiredDeviceIsRediscovered);
FRIEND_TEST_ALL_PREFIXES(DialRegistryTest,
TestRemovingListenerDoesNotClearList);
FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestNetworkEventConnectionLost);
FRIEND_TEST_ALL_PREFIXES(DialRegistryTest,
TestNetworkEventConnectionRestored);
};
} // namespace media_router
#endif // CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_REGISTRY_H_