1*3f982cf4SFabien Sanglard // Copyright 2020 The Chromium Authors. All rights reserved.
2*3f982cf4SFabien Sanglard // Use of this source code is governed by a BSD-style license that can be
3*3f982cf4SFabien Sanglard // found in the LICENSE file.
4*3f982cf4SFabien Sanglard
5*3f982cf4SFabien Sanglard #include "cast/receiver/application_agent.h"
6*3f982cf4SFabien Sanglard
7*3f982cf4SFabien Sanglard #include <utility>
8*3f982cf4SFabien Sanglard
9*3f982cf4SFabien Sanglard #include "cast/common/channel/message_util.h"
10*3f982cf4SFabien Sanglard #include "cast/common/channel/virtual_connection.h"
11*3f982cf4SFabien Sanglard #include "cast/common/public/cast_socket.h"
12*3f982cf4SFabien Sanglard #include "platform/base/tls_credentials.h"
13*3f982cf4SFabien Sanglard #include "platform/base/tls_listen_options.h"
14*3f982cf4SFabien Sanglard #include "util/json/json_helpers.h"
15*3f982cf4SFabien Sanglard #include "util/json/json_serialization.h"
16*3f982cf4SFabien Sanglard #include "util/osp_logging.h"
17*3f982cf4SFabien Sanglard
18*3f982cf4SFabien Sanglard namespace openscreen {
19*3f982cf4SFabien Sanglard namespace cast {
20*3f982cf4SFabien Sanglard namespace {
21*3f982cf4SFabien Sanglard
22*3f982cf4SFabien Sanglard // Returns the first app ID for the given |app|, or the empty string if there is
23*3f982cf4SFabien Sanglard // none.
GetFirstAppId(ApplicationAgent::Application * app)24*3f982cf4SFabien Sanglard std::string GetFirstAppId(ApplicationAgent::Application* app) {
25*3f982cf4SFabien Sanglard const auto& app_ids = app->GetAppIds();
26*3f982cf4SFabien Sanglard return app_ids.empty() ? std::string() : app_ids.front();
27*3f982cf4SFabien Sanglard }
28*3f982cf4SFabien Sanglard
29*3f982cf4SFabien Sanglard } // namespace
30*3f982cf4SFabien Sanglard
ApplicationAgent(TaskRunner * task_runner,DeviceAuthNamespaceHandler::CredentialsProvider * credentials_provider)31*3f982cf4SFabien Sanglard ApplicationAgent::ApplicationAgent(
32*3f982cf4SFabien Sanglard TaskRunner* task_runner,
33*3f982cf4SFabien Sanglard DeviceAuthNamespaceHandler::CredentialsProvider* credentials_provider)
34*3f982cf4SFabien Sanglard : task_runner_(task_runner),
35*3f982cf4SFabien Sanglard auth_handler_(credentials_provider),
36*3f982cf4SFabien Sanglard connection_handler_(&router_, this),
37*3f982cf4SFabien Sanglard message_port_(&router_) {
38*3f982cf4SFabien Sanglard router_.AddHandlerForLocalId(kPlatformReceiverId, this);
39*3f982cf4SFabien Sanglard }
40*3f982cf4SFabien Sanglard
~ApplicationAgent()41*3f982cf4SFabien Sanglard ApplicationAgent::~ApplicationAgent() {
42*3f982cf4SFabien Sanglard OSP_DCHECK(task_runner_->IsRunningOnTaskRunner());
43*3f982cf4SFabien Sanglard
44*3f982cf4SFabien Sanglard idle_screen_app_ = nullptr; // Prevent re-launching the idle screen app.
45*3f982cf4SFabien Sanglard SwitchToApplication({}, {}, nullptr);
46*3f982cf4SFabien Sanglard
47*3f982cf4SFabien Sanglard router_.RemoveHandlerForLocalId(kPlatformReceiverId);
48*3f982cf4SFabien Sanglard }
49*3f982cf4SFabien Sanglard
RegisterApplication(Application * app,bool auto_launch_for_idle_screen)50*3f982cf4SFabien Sanglard void ApplicationAgent::RegisterApplication(Application* app,
51*3f982cf4SFabien Sanglard bool auto_launch_for_idle_screen) {
52*3f982cf4SFabien Sanglard OSP_DCHECK(app);
53*3f982cf4SFabien Sanglard
54*3f982cf4SFabien Sanglard for (const std::string& app_id : app->GetAppIds()) {
55*3f982cf4SFabien Sanglard OSP_DCHECK(!app_id.empty());
56*3f982cf4SFabien Sanglard const auto insert_result = registered_applications_.insert({app_id, app});
57*3f982cf4SFabien Sanglard // The insert must not fail (prior entry for same key).
58*3f982cf4SFabien Sanglard OSP_DCHECK(insert_result.second);
59*3f982cf4SFabien Sanglard }
60*3f982cf4SFabien Sanglard
61*3f982cf4SFabien Sanglard if (auto_launch_for_idle_screen) {
62*3f982cf4SFabien Sanglard OSP_DCHECK(!idle_screen_app_);
63*3f982cf4SFabien Sanglard idle_screen_app_ = app;
64*3f982cf4SFabien Sanglard // Launch the idle screen app, if no app was running.
65*3f982cf4SFabien Sanglard if (!launched_app_) {
66*3f982cf4SFabien Sanglard GoIdle();
67*3f982cf4SFabien Sanglard }
68*3f982cf4SFabien Sanglard }
69*3f982cf4SFabien Sanglard }
70*3f982cf4SFabien Sanglard
UnregisterApplication(Application * app)71*3f982cf4SFabien Sanglard void ApplicationAgent::UnregisterApplication(Application* app) {
72*3f982cf4SFabien Sanglard for (auto it = registered_applications_.begin();
73*3f982cf4SFabien Sanglard it != registered_applications_.end();) {
74*3f982cf4SFabien Sanglard if (it->second == app) {
75*3f982cf4SFabien Sanglard it = registered_applications_.erase(it);
76*3f982cf4SFabien Sanglard } else {
77*3f982cf4SFabien Sanglard ++it;
78*3f982cf4SFabien Sanglard }
79*3f982cf4SFabien Sanglard }
80*3f982cf4SFabien Sanglard
81*3f982cf4SFabien Sanglard if (idle_screen_app_ == app) {
82*3f982cf4SFabien Sanglard idle_screen_app_ = nullptr;
83*3f982cf4SFabien Sanglard }
84*3f982cf4SFabien Sanglard
85*3f982cf4SFabien Sanglard if (launched_app_ == app) {
86*3f982cf4SFabien Sanglard GoIdle();
87*3f982cf4SFabien Sanglard }
88*3f982cf4SFabien Sanglard }
89*3f982cf4SFabien Sanglard
StopApplicationIfRunning(Application * app)90*3f982cf4SFabien Sanglard void ApplicationAgent::StopApplicationIfRunning(Application* app) {
91*3f982cf4SFabien Sanglard if (launched_app_ == app) {
92*3f982cf4SFabien Sanglard GoIdle();
93*3f982cf4SFabien Sanglard }
94*3f982cf4SFabien Sanglard }
95*3f982cf4SFabien Sanglard
OnConnected(ReceiverSocketFactory * factory,const IPEndpoint & endpoint,std::unique_ptr<CastSocket> socket)96*3f982cf4SFabien Sanglard void ApplicationAgent::OnConnected(ReceiverSocketFactory* factory,
97*3f982cf4SFabien Sanglard const IPEndpoint& endpoint,
98*3f982cf4SFabien Sanglard std::unique_ptr<CastSocket> socket) {
99*3f982cf4SFabien Sanglard router_.TakeSocket(this, std::move(socket));
100*3f982cf4SFabien Sanglard }
101*3f982cf4SFabien Sanglard
OnError(ReceiverSocketFactory * factory,Error error)102*3f982cf4SFabien Sanglard void ApplicationAgent::OnError(ReceiverSocketFactory* factory, Error error) {
103*3f982cf4SFabien Sanglard OSP_LOG_ERROR << "Cast agent received socket factory error: " << error;
104*3f982cf4SFabien Sanglard }
105*3f982cf4SFabien Sanglard
OnMessage(VirtualConnectionRouter * router,CastSocket * socket,::cast::channel::CastMessage message)106*3f982cf4SFabien Sanglard void ApplicationAgent::OnMessage(VirtualConnectionRouter* router,
107*3f982cf4SFabien Sanglard CastSocket* socket,
108*3f982cf4SFabien Sanglard ::cast::channel::CastMessage message) {
109*3f982cf4SFabien Sanglard if (message_port_.GetSocketId() == ToCastSocketId(socket) &&
110*3f982cf4SFabien Sanglard !message_port_.client_sender_id().empty() &&
111*3f982cf4SFabien Sanglard message_port_.client_sender_id() == message.destination_id()) {
112*3f982cf4SFabien Sanglard OSP_DCHECK(message_port_.client_sender_id() != kPlatformReceiverId);
113*3f982cf4SFabien Sanglard message_port_.OnMessage(router, socket, std::move(message));
114*3f982cf4SFabien Sanglard return;
115*3f982cf4SFabien Sanglard }
116*3f982cf4SFabien Sanglard
117*3f982cf4SFabien Sanglard if (message.destination_id() != kPlatformReceiverId &&
118*3f982cf4SFabien Sanglard message.destination_id() != kBroadcastId) {
119*3f982cf4SFabien Sanglard return; // Message not for us.
120*3f982cf4SFabien Sanglard }
121*3f982cf4SFabien Sanglard
122*3f982cf4SFabien Sanglard const std::string& ns = message.namespace_();
123*3f982cf4SFabien Sanglard if (ns == kAuthNamespace) {
124*3f982cf4SFabien Sanglard auth_handler_.OnMessage(router, socket, std::move(message));
125*3f982cf4SFabien Sanglard return;
126*3f982cf4SFabien Sanglard }
127*3f982cf4SFabien Sanglard
128*3f982cf4SFabien Sanglard const ErrorOr<Json::Value> request = json::Parse(message.payload_utf8());
129*3f982cf4SFabien Sanglard if (request.is_error() || request.value().type() != Json::objectValue) {
130*3f982cf4SFabien Sanglard return;
131*3f982cf4SFabien Sanglard }
132*3f982cf4SFabien Sanglard
133*3f982cf4SFabien Sanglard Json::Value response;
134*3f982cf4SFabien Sanglard if (ns == kHeartbeatNamespace) {
135*3f982cf4SFabien Sanglard if (HasType(request.value(), CastMessageType::kPing)) {
136*3f982cf4SFabien Sanglard response = HandlePing();
137*3f982cf4SFabien Sanglard }
138*3f982cf4SFabien Sanglard } else if (ns == kReceiverNamespace) {
139*3f982cf4SFabien Sanglard if (request.value()[kMessageKeyRequestId].isNull()) {
140*3f982cf4SFabien Sanglard response = HandleInvalidCommand(request.value());
141*3f982cf4SFabien Sanglard } else if (HasType(request.value(), CastMessageType::kGetAppAvailability)) {
142*3f982cf4SFabien Sanglard response = HandleGetAppAvailability(request.value());
143*3f982cf4SFabien Sanglard } else if (HasType(request.value(), CastMessageType::kGetStatus)) {
144*3f982cf4SFabien Sanglard response = HandleGetStatus(request.value());
145*3f982cf4SFabien Sanglard } else if (HasType(request.value(), CastMessageType::kLaunch)) {
146*3f982cf4SFabien Sanglard response = HandleLaunch(request.value(), socket);
147*3f982cf4SFabien Sanglard } else if (HasType(request.value(), CastMessageType::kStop)) {
148*3f982cf4SFabien Sanglard response = HandleStop(request.value());
149*3f982cf4SFabien Sanglard } else {
150*3f982cf4SFabien Sanglard response = HandleInvalidCommand(request.value());
151*3f982cf4SFabien Sanglard }
152*3f982cf4SFabien Sanglard } else {
153*3f982cf4SFabien Sanglard // Ignore messages for all other namespaces.
154*3f982cf4SFabien Sanglard }
155*3f982cf4SFabien Sanglard
156*3f982cf4SFabien Sanglard if (!response.empty()) {
157*3f982cf4SFabien Sanglard router_.Send(VirtualConnection{message.destination_id(),
158*3f982cf4SFabien Sanglard message.source_id(), ToCastSocketId(socket)},
159*3f982cf4SFabien Sanglard MakeSimpleUTF8Message(ns, json::Stringify(response).value()));
160*3f982cf4SFabien Sanglard }
161*3f982cf4SFabien Sanglard }
162*3f982cf4SFabien Sanglard
IsConnectionAllowed(const VirtualConnection & virtual_conn) const163*3f982cf4SFabien Sanglard bool ApplicationAgent::IsConnectionAllowed(
164*3f982cf4SFabien Sanglard const VirtualConnection& virtual_conn) const {
165*3f982cf4SFabien Sanglard if (virtual_conn.local_id == kPlatformReceiverId) {
166*3f982cf4SFabien Sanglard return true;
167*3f982cf4SFabien Sanglard }
168*3f982cf4SFabien Sanglard if (!launched_app_ || message_port_.client_sender_id().empty()) {
169*3f982cf4SFabien Sanglard // No app currently launched. Or, there is a launched app, but it did not
170*3f982cf4SFabien Sanglard // call MessagePort::SetClient() to indicate it wants messages routed to it.
171*3f982cf4SFabien Sanglard return false;
172*3f982cf4SFabien Sanglard }
173*3f982cf4SFabien Sanglard return virtual_conn.local_id == message_port_.client_sender_id();
174*3f982cf4SFabien Sanglard }
175*3f982cf4SFabien Sanglard
OnClose(CastSocket * socket)176*3f982cf4SFabien Sanglard void ApplicationAgent::OnClose(CastSocket* socket) {
177*3f982cf4SFabien Sanglard if (message_port_.GetSocketId() == ToCastSocketId(socket)) {
178*3f982cf4SFabien Sanglard OSP_VLOG << "Cast agent socket closed.";
179*3f982cf4SFabien Sanglard GoIdle();
180*3f982cf4SFabien Sanglard }
181*3f982cf4SFabien Sanglard }
182*3f982cf4SFabien Sanglard
OnError(CastSocket * socket,Error error)183*3f982cf4SFabien Sanglard void ApplicationAgent::OnError(CastSocket* socket, Error error) {
184*3f982cf4SFabien Sanglard if (message_port_.GetSocketId() == ToCastSocketId(socket)) {
185*3f982cf4SFabien Sanglard OSP_LOG_ERROR << "Cast agent received socket error: " << error;
186*3f982cf4SFabien Sanglard GoIdle();
187*3f982cf4SFabien Sanglard }
188*3f982cf4SFabien Sanglard }
189*3f982cf4SFabien Sanglard
HandlePing()190*3f982cf4SFabien Sanglard Json::Value ApplicationAgent::HandlePing() {
191*3f982cf4SFabien Sanglard Json::Value response;
192*3f982cf4SFabien Sanglard response[kMessageKeyType] = CastMessageTypeToString(CastMessageType::kPong);
193*3f982cf4SFabien Sanglard return response;
194*3f982cf4SFabien Sanglard }
195*3f982cf4SFabien Sanglard
HandleGetAppAvailability(const Json::Value & request)196*3f982cf4SFabien Sanglard Json::Value ApplicationAgent::HandleGetAppAvailability(
197*3f982cf4SFabien Sanglard const Json::Value& request) {
198*3f982cf4SFabien Sanglard Json::Value response;
199*3f982cf4SFabien Sanglard const Json::Value& app_ids = request[kMessageKeyAppId];
200*3f982cf4SFabien Sanglard if (app_ids.isArray()) {
201*3f982cf4SFabien Sanglard response[kMessageKeyRequestId] = request[kMessageKeyRequestId];
202*3f982cf4SFabien Sanglard response[kMessageKeyResponseType] = request[kMessageKeyType];
203*3f982cf4SFabien Sanglard Json::Value& availability = response[kMessageKeyAvailability];
204*3f982cf4SFabien Sanglard for (const Json::Value& app_id : app_ids) {
205*3f982cf4SFabien Sanglard if (app_id.isString()) {
206*3f982cf4SFabien Sanglard const auto app_id_str = app_id.asString();
207*3f982cf4SFabien Sanglard availability[app_id_str] = registered_applications_.count(app_id_str)
208*3f982cf4SFabien Sanglard ? kMessageValueAppAvailable
209*3f982cf4SFabien Sanglard : kMessageValueAppUnavailable;
210*3f982cf4SFabien Sanglard }
211*3f982cf4SFabien Sanglard }
212*3f982cf4SFabien Sanglard }
213*3f982cf4SFabien Sanglard return response;
214*3f982cf4SFabien Sanglard }
215*3f982cf4SFabien Sanglard
HandleGetStatus(const Json::Value & request)216*3f982cf4SFabien Sanglard Json::Value ApplicationAgent::HandleGetStatus(const Json::Value& request) {
217*3f982cf4SFabien Sanglard Json::Value response;
218*3f982cf4SFabien Sanglard PopulateReceiverStatus(&response);
219*3f982cf4SFabien Sanglard response[kMessageKeyRequestId] = request[kMessageKeyRequestId];
220*3f982cf4SFabien Sanglard return response;
221*3f982cf4SFabien Sanglard }
222*3f982cf4SFabien Sanglard
HandleLaunch(const Json::Value & request,CastSocket * socket)223*3f982cf4SFabien Sanglard Json::Value ApplicationAgent::HandleLaunch(const Json::Value& request,
224*3f982cf4SFabien Sanglard CastSocket* socket) {
225*3f982cf4SFabien Sanglard const Json::Value& app_id = request[kMessageKeyAppId];
226*3f982cf4SFabien Sanglard Error error;
227*3f982cf4SFabien Sanglard if (app_id.isString() && !app_id.asString().empty()) {
228*3f982cf4SFabien Sanglard error = SwitchToApplication(app_id.asString(),
229*3f982cf4SFabien Sanglard request[kMessageKeyAppParams], socket);
230*3f982cf4SFabien Sanglard } else {
231*3f982cf4SFabien Sanglard error = Error(Error::Code::kParameterInvalid, kMessageValueBadParameter);
232*3f982cf4SFabien Sanglard }
233*3f982cf4SFabien Sanglard if (!error.ok()) {
234*3f982cf4SFabien Sanglard Json::Value response;
235*3f982cf4SFabien Sanglard response[kMessageKeyRequestId] = request[kMessageKeyRequestId];
236*3f982cf4SFabien Sanglard response[kMessageKeyType] =
237*3f982cf4SFabien Sanglard CastMessageTypeToString(CastMessageType::kLaunchError);
238*3f982cf4SFabien Sanglard response[kMessageKeyReason] = error.message();
239*3f982cf4SFabien Sanglard return response;
240*3f982cf4SFabien Sanglard }
241*3f982cf4SFabien Sanglard
242*3f982cf4SFabien Sanglard // Note: No reply is sent. Instead, the requestor will get a RECEIVER_STATUS
243*3f982cf4SFabien Sanglard // broadcast message from SwitchToApplication(), which is how it will see that
244*3f982cf4SFabien Sanglard // the launch succeeded.
245*3f982cf4SFabien Sanglard return {};
246*3f982cf4SFabien Sanglard }
247*3f982cf4SFabien Sanglard
HandleStop(const Json::Value & request)248*3f982cf4SFabien Sanglard Json::Value ApplicationAgent::HandleStop(const Json::Value& request) {
249*3f982cf4SFabien Sanglard const Json::Value& session_id = request[kMessageKeySessionId];
250*3f982cf4SFabien Sanglard if (session_id.isNull()) {
251*3f982cf4SFabien Sanglard GoIdle();
252*3f982cf4SFabien Sanglard return {};
253*3f982cf4SFabien Sanglard }
254*3f982cf4SFabien Sanglard
255*3f982cf4SFabien Sanglard if (session_id.isString() && launched_app_ &&
256*3f982cf4SFabien Sanglard session_id.asString() == launched_app_->GetSessionId()) {
257*3f982cf4SFabien Sanglard GoIdle();
258*3f982cf4SFabien Sanglard return {};
259*3f982cf4SFabien Sanglard }
260*3f982cf4SFabien Sanglard
261*3f982cf4SFabien Sanglard Json::Value response;
262*3f982cf4SFabien Sanglard response[kMessageKeyRequestId] = request[kMessageKeyRequestId];
263*3f982cf4SFabien Sanglard response[kMessageKeyType] =
264*3f982cf4SFabien Sanglard CastMessageTypeToString(CastMessageType::kInvalidRequest);
265*3f982cf4SFabien Sanglard response[kMessageKeyReason] = kMessageValueInvalidSessionId;
266*3f982cf4SFabien Sanglard return response;
267*3f982cf4SFabien Sanglard }
268*3f982cf4SFabien Sanglard
HandleInvalidCommand(const Json::Value & request)269*3f982cf4SFabien Sanglard Json::Value ApplicationAgent::HandleInvalidCommand(const Json::Value& request) {
270*3f982cf4SFabien Sanglard Json::Value response;
271*3f982cf4SFabien Sanglard if (request[kMessageKeyRequestId].isNull()) {
272*3f982cf4SFabien Sanglard return response;
273*3f982cf4SFabien Sanglard }
274*3f982cf4SFabien Sanglard response[kMessageKeyRequestId] = request[kMessageKeyRequestId];
275*3f982cf4SFabien Sanglard response[kMessageKeyType] =
276*3f982cf4SFabien Sanglard CastMessageTypeToString(CastMessageType::kInvalidRequest);
277*3f982cf4SFabien Sanglard response[kMessageKeyReason] = kMessageValueInvalidCommand;
278*3f982cf4SFabien Sanglard return response;
279*3f982cf4SFabien Sanglard }
280*3f982cf4SFabien Sanglard
SwitchToApplication(std::string app_id,const Json::Value & app_params,CastSocket * socket)281*3f982cf4SFabien Sanglard Error ApplicationAgent::SwitchToApplication(std::string app_id,
282*3f982cf4SFabien Sanglard const Json::Value& app_params,
283*3f982cf4SFabien Sanglard CastSocket* socket) {
284*3f982cf4SFabien Sanglard Error error = Error::Code::kNone;
285*3f982cf4SFabien Sanglard Application* desired_app = nullptr;
286*3f982cf4SFabien Sanglard Application* fallback_app = nullptr;
287*3f982cf4SFabien Sanglard if (!app_id.empty()) {
288*3f982cf4SFabien Sanglard const auto it = registered_applications_.find(app_id);
289*3f982cf4SFabien Sanglard if (it != registered_applications_.end()) {
290*3f982cf4SFabien Sanglard desired_app = it->second;
291*3f982cf4SFabien Sanglard if (desired_app != idle_screen_app_) {
292*3f982cf4SFabien Sanglard fallback_app = idle_screen_app_;
293*3f982cf4SFabien Sanglard }
294*3f982cf4SFabien Sanglard } else {
295*3f982cf4SFabien Sanglard return Error(Error::Code::kItemNotFound, kMessageValueNotFound);
296*3f982cf4SFabien Sanglard }
297*3f982cf4SFabien Sanglard }
298*3f982cf4SFabien Sanglard
299*3f982cf4SFabien Sanglard if (launched_app_ == desired_app) {
300*3f982cf4SFabien Sanglard return error;
301*3f982cf4SFabien Sanglard }
302*3f982cf4SFabien Sanglard
303*3f982cf4SFabien Sanglard if (launched_app_) {
304*3f982cf4SFabien Sanglard launched_app_->Stop();
305*3f982cf4SFabien Sanglard message_port_.SetSocket({});
306*3f982cf4SFabien Sanglard launched_app_ = nullptr;
307*3f982cf4SFabien Sanglard launched_via_app_id_ = {};
308*3f982cf4SFabien Sanglard }
309*3f982cf4SFabien Sanglard
310*3f982cf4SFabien Sanglard if (desired_app) {
311*3f982cf4SFabien Sanglard if (socket) {
312*3f982cf4SFabien Sanglard message_port_.SetSocket(socket->GetWeakPtr());
313*3f982cf4SFabien Sanglard }
314*3f982cf4SFabien Sanglard if (desired_app->Launch(app_id, app_params, &message_port_)) {
315*3f982cf4SFabien Sanglard launched_app_ = desired_app;
316*3f982cf4SFabien Sanglard launched_via_app_id_ = std::move(app_id);
317*3f982cf4SFabien Sanglard } else {
318*3f982cf4SFabien Sanglard error = Error(Error::Code::kUnknownError, kMessageValueSystemError);
319*3f982cf4SFabien Sanglard message_port_.SetSocket({});
320*3f982cf4SFabien Sanglard }
321*3f982cf4SFabien Sanglard }
322*3f982cf4SFabien Sanglard
323*3f982cf4SFabien Sanglard if (!launched_app_ && fallback_app) {
324*3f982cf4SFabien Sanglard app_id = GetFirstAppId(fallback_app);
325*3f982cf4SFabien Sanglard if (fallback_app->Launch(app_id, {}, &message_port_)) {
326*3f982cf4SFabien Sanglard launched_app_ = fallback_app;
327*3f982cf4SFabien Sanglard launched_via_app_id_ = std::move(app_id);
328*3f982cf4SFabien Sanglard }
329*3f982cf4SFabien Sanglard }
330*3f982cf4SFabien Sanglard
331*3f982cf4SFabien Sanglard BroadcastReceiverStatus();
332*3f982cf4SFabien Sanglard
333*3f982cf4SFabien Sanglard return error;
334*3f982cf4SFabien Sanglard }
335*3f982cf4SFabien Sanglard
GoIdle()336*3f982cf4SFabien Sanglard void ApplicationAgent::GoIdle() {
337*3f982cf4SFabien Sanglard std::string app_id;
338*3f982cf4SFabien Sanglard if (idle_screen_app_) {
339*3f982cf4SFabien Sanglard app_id = GetFirstAppId(idle_screen_app_);
340*3f982cf4SFabien Sanglard }
341*3f982cf4SFabien Sanglard SwitchToApplication(app_id, {}, nullptr);
342*3f982cf4SFabien Sanglard }
343*3f982cf4SFabien Sanglard
PopulateReceiverStatus(Json::Value * out)344*3f982cf4SFabien Sanglard void ApplicationAgent::PopulateReceiverStatus(Json::Value* out) {
345*3f982cf4SFabien Sanglard Json::Value& message = *out;
346*3f982cf4SFabien Sanglard message[kMessageKeyType] =
347*3f982cf4SFabien Sanglard CastMessageTypeToString(CastMessageType::kReceiverStatus);
348*3f982cf4SFabien Sanglard Json::Value& status = message[kMessageKeyStatus];
349*3f982cf4SFabien Sanglard
350*3f982cf4SFabien Sanglard if (launched_app_) {
351*3f982cf4SFabien Sanglard Json::Value& details = status[kMessageKeyApplications][0];
352*3f982cf4SFabien Sanglard // If the Application can send/receive messages, the destination for such
353*3f982cf4SFabien Sanglard // messages is provided here, in |transportId|. However, the other end must
354*3f982cf4SFabien Sanglard // first set up the virtual connection by issuing a CONNECT request.
355*3f982cf4SFabien Sanglard // Otherwise, messages will not get routed to the Application by the
356*3f982cf4SFabien Sanglard // VirtualConnectionRouter.
357*3f982cf4SFabien Sanglard if (!message_port_.client_sender_id().empty()) {
358*3f982cf4SFabien Sanglard details[kMessageKeyTransportId] = message_port_.client_sender_id();
359*3f982cf4SFabien Sanglard }
360*3f982cf4SFabien Sanglard details[kMessageKeySessionId] = launched_app_->GetSessionId();
361*3f982cf4SFabien Sanglard details[kMessageKeyAppId] = launched_via_app_id_;
362*3f982cf4SFabien Sanglard details[kMessageKeyUniversalAppId] = launched_via_app_id_;
363*3f982cf4SFabien Sanglard details[kMessageKeyDisplayName] = launched_app_->GetDisplayName();
364*3f982cf4SFabien Sanglard details[kMessageKeyIsIdleScreen] = (launched_app_ == idle_screen_app_);
365*3f982cf4SFabien Sanglard details[kMessageKeyLaunchedFromCloud] = false;
366*3f982cf4SFabien Sanglard std::vector<std::string> app_namespaces =
367*3f982cf4SFabien Sanglard launched_app_->GetSupportedNamespaces();
368*3f982cf4SFabien Sanglard Json::Value& namespaces =
369*3f982cf4SFabien Sanglard (details[kMessageKeyNamespaces] = Json::Value(Json::arrayValue));
370*3f982cf4SFabien Sanglard for (int i = 0, count = app_namespaces.size(); i < count; ++i) {
371*3f982cf4SFabien Sanglard namespaces[i][kMessageKeyName] = std::move(app_namespaces[i]);
372*3f982cf4SFabien Sanglard }
373*3f982cf4SFabien Sanglard }
374*3f982cf4SFabien Sanglard
375*3f982cf4SFabien Sanglard status[kMessageKeyUserEq] = Json::Value(Json::objectValue);
376*3f982cf4SFabien Sanglard
377*3f982cf4SFabien Sanglard // Indicate a fixed 100% volume level.
378*3f982cf4SFabien Sanglard Json::Value& volume = status[kMessageKeyVolume];
379*3f982cf4SFabien Sanglard volume[kMessageKeyControlType] = kMessageValueAttenuation;
380*3f982cf4SFabien Sanglard volume[kMessageKeyLevel] = 1.0;
381*3f982cf4SFabien Sanglard volume[kMessageKeyMuted] = false;
382*3f982cf4SFabien Sanglard volume[kMessageKeyStepInterval] = 0.05;
383*3f982cf4SFabien Sanglard }
384*3f982cf4SFabien Sanglard
BroadcastReceiverStatus()385*3f982cf4SFabien Sanglard void ApplicationAgent::BroadcastReceiverStatus() {
386*3f982cf4SFabien Sanglard Json::Value message;
387*3f982cf4SFabien Sanglard PopulateReceiverStatus(&message);
388*3f982cf4SFabien Sanglard message[kMessageKeyRequestId] = Json::Value(0); // Indicates no requestor.
389*3f982cf4SFabien Sanglard router_.BroadcastFromLocalPeer(
390*3f982cf4SFabien Sanglard kPlatformReceiverId,
391*3f982cf4SFabien Sanglard MakeSimpleUTF8Message(kReceiverNamespace,
392*3f982cf4SFabien Sanglard json::Stringify(message).value()));
393*3f982cf4SFabien Sanglard }
394*3f982cf4SFabien Sanglard
395*3f982cf4SFabien Sanglard ApplicationAgent::Application::~Application() = default;
396*3f982cf4SFabien Sanglard
397*3f982cf4SFabien Sanglard } // namespace cast
398*3f982cf4SFabien Sanglard } // namespace openscreen
399