blob: a6a792aabb5352461b48ee31655020dd225b0ebf [file] [log] [blame]
// Copyright 2020 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 CAST_RECEIVER_APPLICATION_AGENT_H_
#define CAST_RECEIVER_APPLICATION_AGENT_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "cast/common/channel/cast_socket_message_port.h"
#include "cast/common/channel/connection_namespace_handler.h"
#include "cast/common/channel/virtual_connection_manager.h"
#include "cast/common/channel/virtual_connection_router.h"
#include "cast/common/public/cast_socket.h"
#include "cast/receiver/channel/device_auth_namespace_handler.h"
#include "cast/receiver/public/receiver_socket_factory.h"
#include "platform/api/serial_delete_ptr.h"
#include "platform/api/task_runner.h"
#include "platform/base/error.h"
#include "platform/base/ip_address.h"
#include "util/json/json_value.h"
namespace openscreen {
namespace cast {
class CastSocket;
// A service accepting CastSocket connections, and providing a minimal
// implementation of the CastV2 application control protocol to launch receiver
// applications and route messages to/from them.
//
// Workflow: One or more Applications are registered under this ApplicationAgent
// (e.g., a "mirroring" app). Later, a ReceiverSocketFactory (external to this
// class) will listen and establish CastSocket connections, and then pass
// CastSockets to this ApplicationAgent via the OnConnect() method. As each
// connection is made, device authentication will take place. Then, Cast V2
// application messages asking about application availability are received and
// responded to, based on what Applications are registered. Finally, the remote
// may request the LAUNCH of an Application (and later a STOP).
//
// In the meantime, this ApplicationAgent broadcasts RECEIVER_STATUS about what
// application is running. In addition, it attempts to launch an "idle screen"
// Application whenever no other Application is running. The "idle screen"
// Application is usually some kind of screen saver or wallpaper/clock display.
// Registering the "idle screen" Application is optional, and if it's not
// registered, then nothing will be running during idle periods.
class ApplicationAgent final
: public ReceiverSocketFactory::Client,
public CastMessageHandler,
public ConnectionNamespaceHandler::VirtualConnectionPolicy,
public VirtualConnectionRouter::SocketErrorHandler {
public:
class Application {
public:
// Returns the one or more application IDs that are supported. This list
// must not mutate while the Application is registered.
virtual const std::vector<std::string>& GetAppIds() const = 0;
// Launches the application and returns true if successful. |app_id| is the
// specific ID that was used to launch the app, and |app_params| is a
// pass-through for any arbitrary app-specfic structure (or null if not
// provided). If the Application wishes to send/receive messages, it uses
// the provided |message_port| and must call MessagePort::SetClient() before
// any flow will occur.
virtual bool Launch(const std::string& app_id,
const Json::Value& app_params,
MessagePort* message_port) = 0;
// These reflect the current state of the application, and the data is used
// to populate RECEIVER_STATUS messages.
virtual std::string GetSessionId() = 0;
virtual std::string GetDisplayName() = 0;
virtual std::vector<std::string> GetSupportedNamespaces() = 0;
// Stops the application, if running.
virtual void Stop() = 0;
protected:
virtual ~Application();
};
ApplicationAgent(
TaskRunner* task_runner,
DeviceAuthNamespaceHandler::CredentialsProvider* credentials_provider);
~ApplicationAgent() final;
// Return the interface by which the CastSocket inbound traffic is delivered
// into this agent and any running Applications.
CastSocket::Client* cast_socket_client() { return &router_; }
// Registers an Application for launching by this agent. |app| must outlive
// this ApplicationAgent, or until UnregisterApplication() is called.
void RegisterApplication(Application* app,
bool auto_launch_for_idle_screen = false);
void UnregisterApplication(Application* app);
// Stops the given |app| if it is the one currently running. This is used by
// applications that encounter "exit" conditions where they need to STOP
// (e.g., due to timeout of user activity, end of media playback, or fatal
// errors).
void StopApplicationIfRunning(Application* app);
private:
// ReceiverSocketFactory::Client overrides.
void OnConnected(ReceiverSocketFactory* factory,
const IPEndpoint& endpoint,
std::unique_ptr<CastSocket> socket) final;
void OnError(ReceiverSocketFactory* factory, Error error) final;
// CastMessageHandler overrides.
void OnMessage(VirtualConnectionRouter* router,
CastSocket* socket,
::cast::channel::CastMessage message) final;
// ConnectionNamespaceHandler::VirtualConnectionPolicy overrides.
bool IsConnectionAllowed(const VirtualConnection& virtual_conn) const final;
// VirtualConnectionRouter::SocketErrorHandler overrides.
void OnClose(CastSocket* socket) final;
void OnError(CastSocket* socket, Error error) final;
// OnMessage() delegates to these to take action for each |request|. Each of
// these returns a non-empty response message if a reply should be sent back
// to the requestor.
Json::Value HandlePing();
Json::Value HandleGetAppAvailability(const Json::Value& request);
Json::Value HandleGetStatus(const Json::Value& request);
Json::Value HandleLaunch(const Json::Value& request, CastSocket* socket);
Json::Value HandleStop(const Json::Value& request);
Json::Value HandleInvalidCommand(const Json::Value& request);
// Stops the currently-running Application and attempts to launch the
// Application referred to by |app_id|. If this fails, the "idle screen"
// Application will be automatically launched as a failure fall-back. |socket|
// is non-null only when the application switch was caused by a remote LAUNCH
// request.
Error SwitchToApplication(std::string app_id,
const Json::Value& app_params,
CastSocket* socket);
// Stops the currently-running Application and launches the "idle screen."
void GoIdle();
// Populates the given |message| object with the RECEIVER_STATUS fields,
// reflecting the currently-launched app (if any), and a fake volume level
// status.
void PopulateReceiverStatus(Json::Value* message);
// Broadcasts new RECEIVER_STATUS to all endpoints. This is called after an
// Application LAUNCH or STOP.
void BroadcastReceiverStatus();
TaskRunner* const task_runner_;
DeviceAuthNamespaceHandler auth_handler_;
VirtualConnectionManager connection_manager_;
ConnectionNamespaceHandler connection_handler_;
VirtualConnectionRouter router_;
std::map<std::string, Application*> registered_applications_;
Application* idle_screen_app_ = nullptr;
CastSocketMessagePort message_port_;
Application* launched_app_ = nullptr;
std::string launched_via_app_id_;
};
} // namespace cast
} // namespace openscreen
#endif // CAST_RECEIVER_APPLICATION_AGENT_H_