xref: /aosp_15_r20/external/openscreen/cast/sender/cast_platform_client.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/sender/cast_platform_client.h"
6 
7 #include <memory>
8 #include <random>
9 #include <utility>
10 
11 #include "absl/strings/str_cat.h"
12 #include "cast/common/channel/virtual_connection_router.h"
13 #include "cast/common/public/cast_socket.h"
14 #include "cast/common/public/receiver_info.h"
15 #include "util/json/json_serialization.h"
16 #include "util/osp_logging.h"
17 #include "util/stringprintf.h"
18 
19 namespace openscreen {
20 namespace cast {
21 
22 static constexpr std::chrono::seconds kRequestTimeout = std::chrono::seconds(5);
23 
CastPlatformClient(VirtualConnectionRouter * router,ClockNowFunctionPtr clock,TaskRunner * task_runner)24 CastPlatformClient::CastPlatformClient(VirtualConnectionRouter* router,
25                                        ClockNowFunctionPtr clock,
26                                        TaskRunner* task_runner)
27     : sender_id_(MakeUniqueSessionId("sender")),
28       virtual_conn_router_(router),
29       clock_(clock),
30       task_runner_(task_runner) {
31   OSP_DCHECK(virtual_conn_router_);
32   OSP_DCHECK(clock_);
33   OSP_DCHECK(task_runner_);
34   virtual_conn_router_->AddHandlerForLocalId(sender_id_, this);
35 }
36 
~CastPlatformClient()37 CastPlatformClient::~CastPlatformClient() {
38   virtual_conn_router_->RemoveConnectionsByLocalId(sender_id_);
39   virtual_conn_router_->RemoveHandlerForLocalId(sender_id_);
40 
41   for (auto& pending_requests : pending_requests_by_receiver_id_) {
42     for (auto& avail_request : pending_requests.second.availability) {
43       avail_request.callback(avail_request.app_id,
44                              AppAvailabilityResult::kUnknown);
45     }
46   }
47 }
48 
RequestAppAvailability(const std::string & receiver_id,const std::string & app_id,AppAvailabilityCallback callback)49 absl::optional<int> CastPlatformClient::RequestAppAvailability(
50     const std::string& receiver_id,
51     const std::string& app_id,
52     AppAvailabilityCallback callback) {
53   auto entry = socket_id_by_receiver_id_.find(receiver_id);
54   if (entry == socket_id_by_receiver_id_.end()) {
55     callback(app_id, AppAvailabilityResult::kUnknown);
56     return absl::nullopt;
57   }
58   int socket_id = entry->second;
59 
60   int request_id = GetNextRequestId();
61   ErrorOr<::cast::channel::CastMessage> message =
62       CreateAppAvailabilityRequest(sender_id_, request_id, app_id);
63   OSP_DCHECK(message);
64 
65   PendingRequests& pending_requests =
66       pending_requests_by_receiver_id_[receiver_id];
67   auto timeout = std::make_unique<Alarm>(clock_, task_runner_);
68   timeout->ScheduleFromNow(
69       [this, request_id]() { CancelAppAvailabilityRequest(request_id); },
70       kRequestTimeout);
71   pending_requests.availability.push_back(AvailabilityRequest{
72       request_id, app_id, std::move(timeout), std::move(callback)});
73 
74   VirtualConnection virtual_conn{sender_id_, kPlatformReceiverId, socket_id};
75   if (!virtual_conn_router_->GetConnectionData(virtual_conn)) {
76     virtual_conn_router_->AddConnection(virtual_conn,
77                                         VirtualConnection::AssociatedData{});
78   }
79 
80   virtual_conn_router_->Send(std::move(virtual_conn),
81                              std::move(message.value()));
82 
83   return request_id;
84 }
85 
AddOrUpdateReceiver(const ReceiverInfo & receiver,int socket_id)86 void CastPlatformClient::AddOrUpdateReceiver(const ReceiverInfo& receiver,
87                                              int socket_id) {
88   socket_id_by_receiver_id_[receiver.unique_id] = socket_id;
89 }
90 
RemoveReceiver(const ReceiverInfo & receiver)91 void CastPlatformClient::RemoveReceiver(const ReceiverInfo& receiver) {
92   auto pending_requests_it =
93       pending_requests_by_receiver_id_.find(receiver.unique_id);
94   if (pending_requests_it != pending_requests_by_receiver_id_.end()) {
95     for (const AvailabilityRequest& availability :
96          pending_requests_it->second.availability) {
97       availability.callback(availability.app_id,
98                             AppAvailabilityResult::kUnknown);
99     }
100     pending_requests_by_receiver_id_.erase(pending_requests_it);
101   }
102   socket_id_by_receiver_id_.erase(receiver.unique_id);
103 }
104 
CancelRequest(int request_id)105 void CastPlatformClient::CancelRequest(int request_id) {
106   for (auto entry = pending_requests_by_receiver_id_.begin();
107        entry != pending_requests_by_receiver_id_.end(); ++entry) {
108     auto& pending_requests = entry->second;
109     auto it = std::find_if(pending_requests.availability.begin(),
110                            pending_requests.availability.end(),
111                            [request_id](const AvailabilityRequest& request) {
112                              return request.request_id == request_id;
113                            });
114     if (it != pending_requests.availability.end()) {
115       pending_requests.availability.erase(it);
116       break;
117     }
118   }
119 }
120 
OnMessage(VirtualConnectionRouter * router,CastSocket * socket,::cast::channel::CastMessage message)121 void CastPlatformClient::OnMessage(VirtualConnectionRouter* router,
122                                    CastSocket* socket,
123                                    ::cast::channel::CastMessage message) {
124   if (message.payload_type() !=
125           ::cast::channel::CastMessage_PayloadType_STRING ||
126       message.namespace_() != kReceiverNamespace ||
127       message.source_id() != kPlatformReceiverId) {
128     return;
129   }
130   ErrorOr<Json::Value> dict_or_error = json::Parse(message.payload_utf8());
131   if (dict_or_error.is_error()) {
132     return;
133   }
134 
135   Json::Value& dict = dict_or_error.value();
136   absl::optional<int> request_id =
137       MaybeGetInt(dict, JSON_EXPAND_FIND_CONSTANT_ARGS(kMessageKeyRequestId));
138   if (request_id) {
139     auto entry = std::find_if(
140         socket_id_by_receiver_id_.begin(), socket_id_by_receiver_id_.end(),
141         [socket_id =
142              ToCastSocketId(socket)](const std::pair<std::string, int>& entry) {
143           return entry.second == socket_id;
144         });
145     if (entry != socket_id_by_receiver_id_.end()) {
146       HandleResponse(entry->first, request_id.value(), dict);
147     }
148   }
149 }
150 
HandleResponse(const std::string & receiver_id,int request_id,const Json::Value & message)151 void CastPlatformClient::HandleResponse(const std::string& receiver_id,
152                                         int request_id,
153                                         const Json::Value& message) {
154   auto entry = pending_requests_by_receiver_id_.find(receiver_id);
155   if (entry == pending_requests_by_receiver_id_.end()) {
156     return;
157   }
158   PendingRequests& pending_requests = entry->second;
159   auto it = std::find_if(pending_requests.availability.begin(),
160                          pending_requests.availability.end(),
161                          [request_id](const AvailabilityRequest& request) {
162                            return request.request_id == request_id;
163                          });
164   if (it != pending_requests.availability.end()) {
165     // TODO(btolsch): Can all of this manual parsing/checking be cleaned up into
166     // a single parsing API along with other message handling?
167     const Json::Value* maybe_availability =
168         message.find(JSON_EXPAND_FIND_CONSTANT_ARGS(kMessageKeyAvailability));
169     if (maybe_availability && maybe_availability->isObject()) {
170       absl::optional<absl::string_view> result =
171           MaybeGetString(*maybe_availability, &it->app_id[0],
172                          &it->app_id[0] + it->app_id.size());
173       if (result) {
174         AppAvailabilityResult availability_result =
175             AppAvailabilityResult::kUnknown;
176         if (result.value() == kMessageValueAppAvailable) {
177           availability_result = AppAvailabilityResult::kAvailable;
178         } else if (result.value() == kMessageValueAppUnavailable) {
179           availability_result = AppAvailabilityResult::kUnavailable;
180         } else {
181           OSP_VLOG << "Invalid availability result: " << result.value();
182         }
183         it->callback(it->app_id, availability_result);
184       }
185     }
186     pending_requests.availability.erase(it);
187   }
188 }
189 
CancelAppAvailabilityRequest(int request_id)190 void CastPlatformClient::CancelAppAvailabilityRequest(int request_id) {
191   for (auto& entry : pending_requests_by_receiver_id_) {
192     PendingRequests& pending_requests = entry.second;
193     auto it = std::find_if(pending_requests.availability.begin(),
194                            pending_requests.availability.end(),
195                            [request_id](const AvailabilityRequest& request) {
196                              return request.request_id == request_id;
197                            });
198     if (it != pending_requests.availability.end()) {
199       it->callback(it->app_id, AppAvailabilityResult::kUnknown);
200       pending_requests.availability.erase(it);
201     }
202   }
203 }
204 
205 // static
GetNextRequestId()206 int CastPlatformClient::GetNextRequestId() {
207   return next_request_id_++;
208 }
209 
210 // static
211 int CastPlatformClient::next_request_id_ = 0;
212 
213 }  // namespace cast
214 }  // namespace openscreen
215