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