xref: /aosp_15_r20/external/openscreen/cast/receiver/application_agent.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
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