1 /*
2  * This file is part of Wireless Display Software for Linux OS
3  *
4  * Copyright (C) 2014 Intel Corporation.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA
20  */
21 
22 #include <glib-unix.h>
23 #include <algorithm>
24 
25 #include "mirac-broker.hpp"
26 #include "mirac-glib-logging.hpp"
27 
28 struct TimerCallbackData {
TimerCallbackDataTimerCallbackData29   TimerCallbackData(MiracBroker* delegate)
30     : delegate_(delegate), timer_id_(0) {}
31   MiracBroker* delegate_;
32   uint timer_id_;
33 };
34 
35 /* static C callback wrapper */
send_cb(gint fd,GIOCondition condition,gpointer data_ptr)36 gboolean MiracBroker::send_cb (gint fd, GIOCondition condition, gpointer data_ptr)
37 {
38     auto broker = static_cast<MiracBroker**> (data_ptr);
39     return (*broker)->send_cb(fd, condition);
40 }
41 
42 /* static C callback wrapper */
receive_cb(gint fd,GIOCondition condition,gpointer data_ptr)43 gboolean MiracBroker::receive_cb (gint fd, GIOCondition condition, gpointer data_ptr)
44 {
45     auto broker = static_cast<MiracBroker**> (data_ptr);
46     return (*broker)->receive_cb(fd, condition);
47 }
48 
49 /* static C callback wrapper */
listen_cb(gint fd,GIOCondition condition,gpointer data_ptr)50 gboolean MiracBroker::listen_cb (gint fd, GIOCondition condition, gpointer data_ptr)
51 {
52     auto broker = static_cast<MiracBroker**> (data_ptr);
53     return (*broker)->listen_cb(fd, condition);
54 }
55 
56 /* static C callback wrapper */
connect_cb(gint fd,GIOCondition condition,gpointer data_ptr)57 gboolean MiracBroker::connect_cb (gint fd, GIOCondition condition, gpointer data_ptr)
58 {
59     auto broker = static_cast<MiracBroker**> (data_ptr);
60     return (*broker)->connect_cb(fd, condition);
61 }
62 
63 /* static C callback wrapper */
try_connect(gpointer data_ptr)64 gboolean MiracBroker::try_connect (gpointer data_ptr)
65 {
66     auto broker = static_cast<MiracBroker*> (data_ptr);
67     broker->try_connect();
68     return false;
69 }
70 
send_cb(gint fd,GIOCondition condition)71 gboolean MiracBroker::send_cb (gint fd, GIOCondition condition)
72 {
73     try {
74         if (!connection_->Send())
75             return G_SOURCE_CONTINUE;
76     } catch (const MiracConnectionLostException &exception) {
77         on_connection_failure(CONNECTION_LOST);
78     } catch (const std::exception &x) {
79         WDS_WARNING("exception: %s", x.what());
80     }
81     return G_SOURCE_REMOVE;
82 }
83 
84 
receive_cb(gint fd,GIOCondition condition)85 gboolean MiracBroker::receive_cb (gint fd, GIOCondition condition)
86 {
87     std::string msg;
88     try {
89         if (connection_->Receive(msg)) {
90             WDS_VLOG("Received RTSP message:\n%s", msg.c_str());
91             got_message (msg);
92         }
93     } catch (const MiracConnectionLostException &exception) {
94         on_connection_failure(CONNECTION_LOST);
95         return G_SOURCE_REMOVE;
96     }
97     return G_SOURCE_CONTINUE;
98 }
99 
listen_cb(gint fd,GIOCondition condition)100 gboolean MiracBroker::listen_cb (gint fd, GIOCondition condition)
101 {
102     try {
103         connection(network_->Accept());
104         WDS_LOG("connection from: %s", connection_->GetPeerAddress().c_str());
105         on_connected();
106     } catch (const std::exception &x) {
107         WDS_WARNING("exception: %s", x.what());
108     }
109 
110     return G_SOURCE_CONTINUE;
111 }
112 
connect_cb(gint fd,GIOCondition condition)113 gboolean MiracBroker::connect_cb (gint fd, GIOCondition condition)
114 {
115     try {
116         if (!network_->Connect(NULL, NULL))
117             return G_SOURCE_CONTINUE;
118         WDS_LOG("connection success to: %s", network_->GetPeerAddress().c_str());
119         connection(network_.release());
120 
121         /* make sure any network event sources are removed */
122         network(NULL);
123 
124         on_connected();
125     } catch (const std::exception &x) {
126         gdouble elapsed = 1000 * g_timer_elapsed(connect_timer_, NULL);
127         if (elapsed + connect_wait_ > connect_timeout_) {
128             on_connection_failure(CONNECTION_TIMEOUT);
129         } else {
130             connect_wait_id_ = g_timeout_add (connect_wait_, try_connect, this);
131         }
132     }
133     return G_SOURCE_REMOVE;
134 }
135 
network(MiracNetwork * connection)136 void MiracBroker::network(MiracNetwork *connection)
137 {
138     while (g_source_remove_by_user_data(&network_source_ptr_))
139         ;
140     network_.reset(connection);
141 }
142 
connection(MiracNetwork * connection)143 void MiracBroker::connection(MiracNetwork *connection)
144 {
145     while (g_source_remove_by_user_data(&connection_source_ptr_))
146         ;
147     connection_.reset(connection);
148 
149     if (connection_)
150         g_unix_fd_add(connection_->GetHandle(), G_IO_IN,
151                       receive_cb, &connection_source_ptr_);
152 }
153 
try_connect()154 void MiracBroker::try_connect()
155 {
156     WDS_LOG("Trying to connect...");
157 
158     connect_wait_id_ = 0;
159     network(new MiracNetwork());
160 
161     if (network_->Connect(peer_address_.c_str(), peer_port_.c_str())) {
162         g_unix_fd_add(network_->GetHandle(), G_IO_OUT,
163                       MiracBroker::send_cb, &network_source_ptr_);
164     } else {
165         g_unix_fd_add(network_->GetHandle(), G_IO_OUT,
166                       MiracBroker::connect_cb, &network_source_ptr_);
167     }
168 }
169 
get_host_port() const170 unsigned short MiracBroker::get_host_port() const
171 {
172     return network_->GetHostPort();
173 }
174 
get_peer_address() const175 std::string MiracBroker::get_peer_address() const
176 {
177     return connection_->GetPeerAddress();
178 }
179 
MiracBroker(const std::string & listen_port)180 MiracBroker::MiracBroker (const std::string& listen_port):
181     connect_timer_(NULL)
182 {
183     network_source_ptr_ = this;
184     connection_source_ptr_ = this;
185 
186     network(new MiracNetwork());
187 
188     network_->Bind(NULL, listen_port.c_str());
189     g_unix_fd_add(network_->GetHandle(), G_IO_IN,
190                   MiracBroker::listen_cb, &network_source_ptr_);
191 }
192 
MiracBroker(const std::string & peer_address,const std::string & peer_port,uint timeout)193 MiracBroker::MiracBroker(const std::string& peer_address, const std::string& peer_port, uint timeout):
194     peer_address_(peer_address),
195     peer_port_(peer_port),
196     connect_timeout_(timeout)
197 {
198     network_source_ptr_ = this;
199     connection_source_ptr_ = this;
200     connect_timer_ = g_timer_new();
201     try_connect();
202 }
203 
~MiracBroker()204 MiracBroker::~MiracBroker ()
205 {
206     network(NULL);
207     connection(NULL);
208 
209     if (connect_timer_) {
210         g_timer_destroy(connect_timer_);
211         connect_timer_ = NULL;
212     }
213 
214     if (connect_wait_id_ > 0) {
215         g_source_remove(connect_wait_id_);
216         connect_wait_id_ = 0;
217     }
218     while (!timers_.empty())
219         g_source_remove(timers_.front());
220 }
221 
SendRTSPData(const std::string & data)222 void MiracBroker::SendRTSPData(const std::string& data) {
223   WDS_VLOG("Sending RTSP message:\n%s", data.c_str());
224 
225   if (connection_ && !connection_->Send(data))
226       g_unix_fd_add(connection_->GetHandle(), G_IO_OUT,
227                     send_cb, &connection_source_ptr_);
228 }
229 
GetLocalIPAddress() const230 std::string MiracBroker::GetLocalIPAddress() const {
231   return "127.0.0.1";  // FIXME : return the actual local IP address.
232 }
233 
on_timeout_remove(gpointer user_data)234 void MiracBroker::on_timeout_remove(gpointer user_data) {
235   TimerCallbackData* data = static_cast<TimerCallbackData*>(user_data);
236   auto& timers = data->delegate_->timers_;
237   auto it = std::find(timers.begin(), timers.end(), data->timer_id_);
238   if (it != timers.end())
239     timers.erase(it);
240   delete data;
241 }
242 
on_timeout(gpointer user_data)243 static gboolean on_timeout(gpointer user_data) {
244   TimerCallbackData* data = static_cast<TimerCallbackData*>(user_data);
245   data->delegate_->OnTimeout(data->timer_id_);
246   return FALSE;
247 }
248 
OnTimeout(uint timer_id)249 void MiracBroker::OnTimeout(uint timer_id) {
250   Peer()->OnTimerEvent(timer_id);
251 }
252 
CreateTimer(int seconds)253 uint MiracBroker::CreateTimer(int seconds) {
254   TimerCallbackData* data = new TimerCallbackData(this);
255   uint timer_id = g_timeout_add_seconds_full(
256                         G_PRIORITY_DEFAULT,
257                         seconds,
258                         on_timeout,
259                         data,
260                         on_timeout_remove);
261   if (timer_id > 0) {
262     data->timer_id_ = timer_id;
263     timers_.push_back(timer_id);
264   } else {
265     delete data;
266   }
267 
268   return timer_id;
269 }
270 
ReleaseTimer(uint timer_id)271 void MiracBroker::ReleaseTimer(uint timer_id) {
272   if (timer_id > 0) {
273     auto it = std::find(timers_.begin(), timers_.end(), timer_id);
274     if (it != timers_.end() )
275       g_source_remove(*it);
276   }
277 }
278 
GetNextCSeq(int * initial_peer_cseq) const279 int MiracBroker::GetNextCSeq(int* initial_peer_cseq) const {
280   static int send_cseq_;
281   ++send_cseq_;
282   if (initial_peer_cseq && send_cseq_ == *initial_peer_cseq)
283     send_cseq_ *= 2;
284 
285   return send_cseq_;
286 }
287 
288