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