1 /*
2  * libjingle
3  * Copyright 2011, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "talk/app/webrtc/roapsignaling.h"
29 
30 #include <utility>
31 
32 #include "talk/app/webrtc/jsepsessiondescription.h"
33 #include "talk/app/webrtc/mediastreamsignaling.h"
34 #include "talk/app/webrtc/streamcollectionimpl.h"
35 #include "talk/base/helpers.h"
36 #include "talk/base/logging.h"
37 #include "talk/base/messagequeue.h"
38 #include "talk/session/phone/channelmanager.h"
39 
40 using talk_base::scoped_refptr;
41 
42 namespace webrtc {
43 
44 enum {
45   MSG_SEND_QUEUED_OFFER = 1,
46   MSG_GENERATE_ANSWER = 2,
47 };
48 
RoapSignaling(MediaStreamSignaling * mediastream_signaling,JsepInterface * provider)49 RoapSignaling::RoapSignaling(
50     MediaStreamSignaling* mediastream_signaling,
51     JsepInterface* provider)
52     : stream_signaling_(mediastream_signaling),
53       provider_(provider),
54       state_(kNew),
55       received_pre_offer_(false),
56       local_streams_(StreamCollection::Create()) {
57 }
58 
~RoapSignaling()59 RoapSignaling::~RoapSignaling() {}
60 
OnIceComplete()61 void RoapSignaling::OnIceComplete() {
62   if (!VERIFY(state_ == kInitializing))
63     return;
64   // If we have a queued remote offer we need to handle this first.
65   if (received_pre_offer_) {
66     received_pre_offer_ = false;
67     SendAnswer();
68   } else if (!queued_local_streams_.empty()) {
69     // Else CreateOffer have been called.
70     SendOffer(provider_->local_description());
71   } else {
72     ChangeState(kIdle);
73   }
74 }
75 
OnIceCandidate(const IceCandidateInterface *)76 void RoapSignaling::OnIceCandidate(
77     const IceCandidateInterface* /*candidate*/) {
78   // Ignore all candidates. We only care about when all
79   // candidates have been collected.
80 }
81 
ChangeState(State new_state)82 void RoapSignaling::ChangeState(State new_state) {
83   state_ = new_state;
84   SignalStateChange(state_);
85 }
86 
ProcessSignalingMessage(const std::string & message,StreamCollectionInterface * local_streams)87 void RoapSignaling::ProcessSignalingMessage(
88     const std::string& message,
89     StreamCollectionInterface* local_streams) {
90   RoapSession::ParseResult result = roap_session_.Parse(message);
91 
92   // Signal an error message and return if a message is received after shutdown
93   // or it is not an ok message that is received during shutdown.
94   // No other messages from the remote peer can be processed in these states.
95   if (state_ == kShutdownComplete ||
96       (state_ == kShutingDown && result != RoapSession::kOk)) {
97     SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(kNoMatch));
98     return;
99   }
100 
101   switch (result) {
102     case RoapSession::kOffer: {
103       if (state_ == kWaitingForAnswer) {
104         // Message received out of order or Glare occurred and the decision was
105         // to use the incoming offer.
106         LOG(LS_INFO) << "Received offer while waiting for answer.";
107       }
108 
109       // Provide the remote session description and the remote candidates from
110       // the parsed ROAP message to the |provider_|.
111       if (!ProcessRemoteDescription(roap_session_.RemoteDescription(),
112                                     JsepInterface::kOffer)) {
113         break;
114       }
115 
116       InitializeSendingAnswer(local_streams);
117       break;
118     }
119     case RoapSession::kAnswerMoreComing: {
120       // We ignore this message for now and wait for the complete result.
121       LOG(LS_INFO) << "Received answer more coming.";
122       break;
123     }
124     case RoapSession::kAnswer: {
125       if (state_ != kWaitingForAnswer) {
126         LOG(LS_WARNING) << "Received an unexpected answer.";
127         return;
128       }
129 
130       // Pop the first item of queued StreamCollections containing local
131       // MediaStreams that just have been negotiated.
132       scoped_refptr<StreamCollectionInterface> streams(
133           queued_local_streams_.front());
134       queued_local_streams_.pop_front();
135 
136       // If this is an answer to an initial offer SetLocalDescription have
137       // already been called.
138       if (local_desc_.get()) {
139         // Hand the ownership of the local session description to |provider_|.
140         provider_->SetLocalDescription(JsepInterface::kOffer,
141                                        local_desc_.release());
142       }
143 
144       // Provide the remote session description and the remote candidates from
145       // the parsed ROAP message to the |provider_|.
146       if (!ProcessRemoteDescription(roap_session_.RemoteDescription(),
147                                     JsepInterface::kAnswer)) {
148         break;
149       }
150 
151       // Let the remote peer know we have received the answer.
152       SignalNewPeerConnectionMessage(roap_session_.CreateOk());
153       // Check if we have more offers waiting in the queue.
154       if (!queued_local_streams_.empty()) {
155         // Send the next offer.
156         InitializeSendingOffer();
157       } else {
158         ChangeState(kIdle);
159       }
160       break;
161     }
162     case RoapSession::kOk: {
163       if (state_ == kWaitingForOK) {
164         ChangeState(kIdle);
165         // Check if we have an updated offer waiting in the queue.
166         if (!queued_local_streams_.empty())
167           InitializeSendingOffer();
168       } else if (state_ == kShutingDown) {
169         ChangeState(kShutdownComplete);
170       }
171       break;
172     }
173     case RoapSession::kParseConflict: {
174       SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(
175           kConflict));
176       break;
177     }
178     case RoapSession::kParseDoubleConflict: {
179       SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(
180           kDoubleConflict));
181 
182       // Recreate the offer with new sequence values etc.
183       InitializeSendingOffer();
184       break;
185     }
186     case RoapSession::kError: {
187       if (roap_session_.RemoteError() != kConflict &&
188           roap_session_.RemoteError() != kDoubleConflict) {
189         SignalErrorMessageReceived(roap_session_.RemoteError());
190         // An error have occurred that we can't do anything about.
191         // Reset the state and wait for user action.
192         queued_local_streams_.clear();
193         ChangeState(kIdle);
194       }
195       break;
196     }
197     case RoapSession::kShutDown: {
198       DoShutDown();
199       ChangeState(kShutdownComplete);
200       SignalNewPeerConnectionMessage(roap_session_.CreateOk());
201       break;
202     }
203     case RoapSession::kInvalidMessage: {
204       SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(
205           kNoMatch));
206       return;
207     }
208   }
209 }
210 
CreateOffer(StreamCollectionInterface * local_streams)211 void RoapSignaling::CreateOffer(StreamCollectionInterface* local_streams) {
212   if (!VERIFY(state_ != kShutingDown && state_ != kShutdownComplete)) {
213     return;
214   }
215 
216   queued_local_streams_.push_back(local_streams);
217   if (state_ == kNew || state_ == kIdle) {
218     InitializeSendingOffer();
219   }
220   return;
221 }
222 
SendShutDown()223 void RoapSignaling::SendShutDown() {
224   DoShutDown();
225   SignalNewPeerConnectionMessage(roap_session_.CreateShutDown());
226 }
227 
InitializeSendingOffer()228 void RoapSignaling::InitializeSendingOffer() {
229   ASSERT(!queued_local_streams_.empty());
230 
231   scoped_refptr<StreamCollectionInterface> local_streams(
232       queued_local_streams_.front());
233 
234   stream_signaling_->SetLocalStreams(local_streams);
235   local_desc_.reset(provider_->CreateOffer(MediaHints()));
236 
237   // If we are still in state kNew, we need to start the ice negotiation and
238   // wait until we have our local candidates before we can send the offer.
239   // The offer is sent in OnIceComplete.
240   if (state_ == kNew) {
241     ChangeState(kInitializing);
242     provider_->SetLocalDescription(JsepInterface::kOffer,
243                                    local_desc_.release());
244     // Start Ice and set the local session description for the first time.
245     provider_->StartIce(JsepInterface::kUseAll);
246     return;
247   }
248   SendOffer(local_desc_.get());
249 }
250 
SendOffer(const SessionDescriptionInterface * local_desc)251 void RoapSignaling::SendOffer(const SessionDescriptionInterface* local_desc) {
252   ChangeState(kWaitingForAnswer);
253   std::string sdp;
254   local_desc->ToString(&sdp);
255   SignalNewPeerConnectionMessage(roap_session_.CreateOffer(sdp));
256 }
257 
InitializeSendingAnswer(StreamCollectionInterface * local_streams)258 void RoapSignaling::InitializeSendingAnswer(
259     StreamCollectionInterface* local_streams) {
260   if (state_ == kInitializing) {
261     LOG(LS_WARNING) << "Unexpected offer received";
262     SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(kRefused));
263     return;
264   }
265 
266   // If we are still in state kNew, we need to start the ice negotiation and
267   // wait until we have our local candidates before we can send the answer.
268   // We need to change the state here since
269   // |provider_->SetLocalDescription| Sends tasks to other threads and we
270   // therefore run the risk of receiving tasks from other threads while doing so.
271   bool start_ice = false;
272   if (state_ == kNew) {
273     start_ice = true;
274     ChangeState(kInitializing);
275   }
276   // Clean up all queued collections of local streams.
277   queued_local_streams_.clear();
278 
279   stream_signaling_->SetLocalStreams(local_streams);
280   // Create an local session description based on |local_streams|.
281   SessionDescriptionInterface* local_desc(provider_->CreateAnswer(
282       MediaHints(), provider_->remote_description()));
283   if (!local_desc || !provider_->SetLocalDescription(JsepInterface::kAnswer,
284                                                      local_desc)) {
285     LOG(LS_WARNING) << "Answer to Roap offer failed";
286     SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(kRefused));
287     return;
288   }
289 
290   if (start_ice) {
291     if (!provider_->StartIce(JsepInterface::kUseAll)) {
292       SignalNewPeerConnectionMessage(
293           roap_session_.CreateErrorMessage(kRefused));
294       return;
295     }
296     received_pre_offer_ = true;
297     // The answer is sent in OnIceComplete.
298     return;
299   }
300   SendAnswer();
301 }
302 
SendAnswer()303 void RoapSignaling::SendAnswer() {
304   std::string sdp;
305   provider_->local_description()->ToString(&sdp);
306   received_pre_offer_ = false;
307   ChangeState(kWaitingForOK);
308   SignalNewPeerConnectionMessage(roap_session_.CreateAnswer(sdp));
309 }
310 
DoShutDown()311 void RoapSignaling::DoShutDown() {
312   ChangeState(kShutingDown);
313   queued_local_streams_.clear();
314 
315   stream_signaling_->SetLocalStreams(NULL);
316   // Create new empty session descriptions without StreamParams.
317   // By applying these descriptions we don't send or receive any streams.
318   SessionDescriptionInterface* local_desc =
319       provider_->CreateOffer(MediaHints());
320   SessionDescriptionInterface* remote_desc =
321       provider_->CreateAnswer(MediaHints(), local_desc);
322 
323   provider_->SetRemoteDescription(JsepInterface::kOffer, remote_desc);
324   provider_->SetLocalDescription(JsepInterface::kAnswer, local_desc);
325 }
326 
ProcessRemoteDescription(const std::string & sdp,JsepInterface::Action action)327 bool RoapSignaling::ProcessRemoteDescription(const std::string& sdp,
328                                              JsepInterface::Action action) {
329   // Provide the remote session description and the remote candidates from
330   // the parsed ROAP message to the |provider_|.
331   SessionDescriptionInterface* desc = CreateSessionDescription(sdp);
332   bool ret = provider_->SetRemoteDescription(action, desc);
333   if (!ret)
334     SignalNewPeerConnectionMessage(roap_session_.CreateErrorMessage(kRefused));
335   return ret;
336 }
337 
338 }  // namespace webrtc
339