blob: 38a2b0909f92b92235fce4d8e498c494bd536cb7 [file] [log] [blame]
// Copyright 2017 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_MEDIA_SINK_SERVICE_IMPL_H_
#define CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_MEDIA_SINK_SERVICE_IMPL_H_
#include <memory>
#include <set>
#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "chrome/browser/media/router/discovery/dial/device_description_service.h"
#include "chrome/browser/media/router/discovery/dial/dial_app_discovery_service.h"
#include "chrome/browser/media/router/discovery/dial/dial_media_sink_service.h"
#include "chrome/browser/media/router/discovery/dial/dial_registry.h"
#include "chrome/browser/media/router/discovery/media_sink_discovery_metrics.h"
#include "components/media_router/common/discovery/media_sink_service_base.h"
#include "components/media_router/common/discovery/media_sink_service_util.h"
namespace media_router {
class DeviceDescriptionService;
class DialRegistry;
// A service which can be used to start background discovery and resolution of
// DIAL devices (Smart TVs, Game Consoles, etc.).
// This class may be created on any thread. All methods, unless otherwise noted,
// must be invoked on the SequencedTaskRunner given by |task_runner()|.
class DialMediaSinkServiceImpl : public MediaSinkServiceBase,
public DialRegistry::Client {
public:
// Callbacks invoked when the list of available sinks for |app_name| changes.
// The client can call |GetAvailableSinks()| to obtain the latest sink list.
// |app_name|: app name, e.g. YouTube.
// TODO(imcheng): Move sink query logic into DialAppDiscoveryService and
// have it use MediaSinkServiceBase::Observer to observe sinks.
using SinkQueryByAppCallbackList =
base::RepeatingCallbackList<void(const std::string&)>;
using SinkQueryByAppCallback = SinkQueryByAppCallbackList::CallbackType;
// Represents DIAL app status on receiver device.
enum SinkAppStatus { kUnknown = 0, kAvailable, kUnavailable };
// |on_sinks_discovered_cb|: Callback for MediaSinkServiceBase.
// Note that both callbacks are invoked on |task_runner|.
// |task_runner|: The SequencedTaskRunner this class runs in.
DialMediaSinkServiceImpl(
const OnSinksDiscoveredCallback& on_sinks_discovered_cb,
const scoped_refptr<base::SequencedTaskRunner>& task_runner);
~DialMediaSinkServiceImpl() override;
virtual void Start();
// MediaSinkServiceBase implementation.
void OnUserGesture() override;
// Returns the SequencedTaskRunner that should be used to invoke methods on
// this instance. Can be invoked on any thread.
scoped_refptr<base::SequencedTaskRunner> task_runner() {
return task_runner_;
}
virtual DialAppDiscoveryService* app_discovery_service();
// Registers |callback| to callback list entry in |sink_queries_|, with the
// key |app_name|. Caller owns the returned subscription and is responsible
// for destroying when it wants to unregister |callback|. Marked virtual for
// tests.
virtual base::CallbackListSubscription StartMonitoringAvailableSinksForApp(
const std::string& app_name,
const SinkQueryByAppCallback& callback);
// Returns the current list of sinks compatible with |app_name|. The caller
// can call this method after calling |StartMonitoringAvailableSinksForApp()|
// to obtain the initial list, or when the callback fires to get the updated
// list.
// Marked virtual for tests.
virtual std::vector<MediaSinkInternal> GetAvailableSinks(
const std::string& app_name) const;
void BindLogger(mojo::PendingRemote<mojom::Logger> pending_remote);
protected:
void SetDescriptionServiceForTest(
std::unique_ptr<DeviceDescriptionService> description_service);
void SetAppDiscoveryServiceForTest(
std::unique_ptr<DialAppDiscoveryService> app_discovery_service);
private:
friend class DialMediaSinkServiceImplTest;
friend class MockDialMediaSinkServiceImpl;
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
OnDeviceDescriptionRestartsTimer);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
OnDialDeviceListRestartsTimer);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
OnDeviceDescriptionAvailable);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
OnDeviceDescriptionAvailableIPAddressChanged);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
StartStopMonitoringAvailableSinksForApp);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
OnDialAppInfoAvailableNoStartMonitoring);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
OnDialAppInfoAvailableNoSink);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
OnDialAppInfoAvailableSinksAdded);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
OnDialAppInfoAvailableSinksRemoved);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
OnDialAppInfoAvailableWithAlreadyAvailableSinks);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
StartAfterStopMonitoringForApp);
FRIEND_TEST_ALL_PREFIXES(DialMediaSinkServiceImplTest,
FetchDialAppInfoWithDiscoveryOnlySink);
// DialRegistry::Client implementation
void OnDialDeviceList(const DialRegistry::DeviceList& devices) override;
void OnDialError(DialRegistry::DialErrorCode type) override;
// Called when description service successfully fetches and parses device
// description XML. Restart |finish_timer_| if it is not running.
void OnDeviceDescriptionAvailable(
const DialDeviceData& device_data,
const ParsedDialDeviceDescription& description_data);
// Called when fails to fetch or parse device description XML.
void OnDeviceDescriptionError(const DialDeviceData& device,
const std::string& error_message);
// Called when app discovery service finishes fetching and parsing app info
// XML.
void OnAppInfoParseCompleted(const std::string& sink_id,
const std::string& app_name,
DialAppInfoResult result);
// Queries app status of |app_name| on |dial_sink|.
void FetchAppInfoForSink(const MediaSinkInternal& dial_sink,
const std::string& app_name);
// Issues HTTP request to get status of all registered apps on current sinks.
void RescanAppInfo();
// Helper function to get app status from |app_statuses_|.
SinkAppStatus GetAppStatus(const std::string& sink_id,
const std::string& app_name) const;
// Helper function to set app status in |app_statuses_|.
void SetAppStatus(const std::string& sink_id,
const std::string& app_name,
SinkAppStatus app_status);
void MaybeRemoveSinkQueryCallbackList(
const std::string& app_name,
SinkQueryByAppCallbackList* callback_list);
// MediaSinkServiceBase implementation.
void OnDiscoveryComplete() override;
void RecordDeviceCounts() override;
// Initialized in |Start()|.
std::unique_ptr<DialRegistry> dial_registry_;
// Initialized in |Start()|.
std::unique_ptr<DeviceDescriptionService> description_service_;
// Initialized in |Start()|.
std::unique_ptr<DialAppDiscoveryService> app_discovery_service_;
// Device data list from current round of discovery.
DialRegistry::DeviceList current_devices_;
// Sinks that are added during the latest round of discovery. In
// |OnDiscoveryCompleted()| this will be merged into
// |MediaSinkServiceBase::sinks_| and then cleared.
base::flat_map<MediaSink::Id, MediaSinkInternal> latest_sinks_;
// Map of app status, keyed by <sink id:app name>.
base::flat_map<std::string, SinkAppStatus> app_statuses_;
// Set of sink queries keyed by app name.
base::flat_map<std::string, std::unique_ptr<SinkQueryByAppCallbackList>>
sink_queries_;
// Mojo Remote to the logger owned by the Media Router. The Remote is not
// bound until |BindLogger()| is called. Always check if |logger_.is_bound()|
// is true before using.
mojo::Remote<mojom::Logger> logger_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
DialDeviceCountMetrics metrics_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace media_router
#endif // CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_MEDIA_SINK_SERVICE_IMPL_H_