1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
|
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.protocol.jabber;
import java.util.*;
import org.jivesoftware.smack.packet.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.gtalk.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.media.*;
/**
* A Google Talk implementation of the <tt>Call</tt> abstract class
* encapsulating Google Talk sessions.
*
* @author Sebastien Vincent
*/
public class CallGTalkImpl
extends MediaAwareCall<
CallPeerGTalkImpl,
OperationSetBasicTelephonyJabberImpl,
ProtocolProviderServiceJabberImpl>
{
/**
* If the first callPeer is a Google Voice (without resource) ones.
*/
private boolean firstCallPeerIsGV = false;
/**
* Initializes a new <tt>CallGTalkImpl</tt> instance belonging to
* <tt>sourceProvider</tt> and associated with the jingle session with the
* specified <tt>sessionID</tt>. If the new instance corresponds to an
* incoming Google Talk session, then the sessionID would come from there.
* Otherwise, one could generate one using {@link SessionIQ#generateSID()}.
*
* @param parentOpSet the {@link OperationSetBasicTelephonyJabberImpl}
* instance in the context of which this call has been created.
*/
protected CallGTalkImpl(
OperationSetBasicTelephonyJabberImpl parentOpSet)
{
super(parentOpSet);
//let's add ourselves to the calls repo. we are doing it ourselves just
//to make sure that no one ever forgets.
parentOpSet.getGTalkActiveCallsRepository().addCall(this);
}
/**
* Determines if this call contains a peer whose corresponding session has
* the specified <tt>sid</tt>.
*
* @param sid the ID of the session whose peer we are looking for.
*
* @return <tt>true</tt> if this call contains a peer with the specified
* Google Talk <tt>sid</tt> and false otherwise.
*/
public boolean containsSessionID(String sid)
{
return (getPeer(sid) != null);
}
/**
* Returns the peer whose corresponding session has the specified
* <tt>sid</tt>.
*
* @param sid the ID of the session whose peer we are looking for.
*
* @return the {@link CallPeerGTalkImpl} with the specified Google Talk
* <tt>sid</tt> and <tt>null</tt> if no such peer exists in this call.
*/
public CallPeerGTalkImpl getPeer(String sid)
{
for(CallPeerGTalkImpl peer : getCallPeersVector())
{
if (peer.getSessionID().equals(sid))
return peer;
}
return null;
}
/**
* Returns the peer whose corresponding session-init ID has the specified
* <tt>id</tt>.
*
* @param id the ID of the session-init IQ whose peer we are looking for.
*
* @return the {@link CallPeerGTalkImpl} with the specified IQ
* <tt>id</tt> and <tt>null</tt> if no such peer exists in this call.
*/
public CallPeerGTalkImpl getPeerBySessInitPacketID(String id)
{
for(CallPeerGTalkImpl peer : getCallPeersVector())
{
if (peer.getSessInitID().equals(id))
return peer;
}
return null;
}
/**
* Creates a new Google Talk call peer and sends a RINGING response.
*
* @param sessionIQ the {@link SessionIQ} that created the session.
*
* @return the newly created {@link CallPeerGTalkImpl} (the one that sent
* the INVITE).
*/
public CallPeerGTalkImpl processGTalkInitiate(SessionIQ sessionIQ)
{
String remoteParty = sessionIQ.getInitiator();
//according to the Jingle spec initiator may be null.
if (remoteParty == null)
remoteParty = sessionIQ.getFrom();
CallPeerGTalkImpl callPeer = new CallPeerGTalkImpl(remoteParty, this);
addCallPeer(callPeer);
//before notifying about this call, make sure that it looks alright
callPeer.processSessionInitiate(sessionIQ);
// if paranoia is set, to accept the call we need to know that
// the other party has support for media encryption
if(getProtocolProvider().getAccountID().getAccountPropertyBoolean(
ProtocolProviderFactory.MODE_PARANOIA, false)
&& callPeer.getMediaHandler().getAdvertisedEncryptionMethods().length
== 0)
{
//send an error response;
String reasonText =
JabberActivator.getResources().getI18NString(
"service.gui.security.encryption.required");
SessionIQ errResp = GTalkPacketFactory.createSessionTerminate(
sessionIQ.getTo(),
sessionIQ.getFrom(),
sessionIQ.getID(),
Reason.SECURITY_ERROR,
reasonText);
callPeer.setState(CallPeerState.FAILED, reasonText);
getProtocolProvider().getConnection().sendPacket(errResp);
return null;
}
if( callPeer.getState() == CallPeerState.FAILED)
return null;
callPeer.setState( CallPeerState.INCOMING_CALL );
// if this was the first peer we added in this call then the call is
// new and we also need to notify everyone of its creation.
if(this.getCallPeerCount() == 1 && this.getCallGroup() == null)
parentOpSet.fireCallEvent( CallEvent.CALL_RECEIVED, this);
// Manages auto answer with "audio only", or "audio / video" answer.
OperationSetAutoAnswerJabberImpl autoAnswerOpSet
= (OperationSetAutoAnswerJabberImpl)
this.getProtocolProvider()
.getOperationSet(OperationSetBasicAutoAnswer.class);
if(autoAnswerOpSet != null)
{
// With Gtalk, we do not actually supports the detection if the
// incoming call is a video call (cf. the fireCallEvent above with
// only 2 arguments). Thus, we set the auto-answer video
// parameter to false.
autoAnswerOpSet.autoAnswer(this, false);
}
return callPeer;
}
/**
* Creates a <tt>CallPeerGTalkImpl</tt> from <tt>calleeJID</tt> and sends
* them <tt>initiate</tt> IQ request.
*
* @param calleeJID the party that we would like to invite to this call.
* @param sessionInitiateExtensions a collection of additional and optional
* <tt>PacketExtension</tt>s to be added to the <tt>initiate</tt>
* {@link SessionIQ} which is to init this <tt>CallJabberImpl</tt>
*
* @return the newly created <tt>Call</tt> corresponding to
* <tt>calleeJID</tt>. All following state change events will be
* delivered through this call peer.
*
* @throws OperationFailedException with the corresponding code if we fail
* to create the call.
*/
public CallPeerGTalkImpl initiateGTalkSession(
String calleeJID,
Iterable<PacketExtension> sessionInitiateExtensions)
throws OperationFailedException
{
// create the session-initiate IQ
CallPeerGTalkImpl callPeer = new CallPeerGTalkImpl(calleeJID, this);
if(!firstCallPeerIsGV)
firstCallPeerIsGV = calleeJID.endsWith(
ProtocolProviderServiceJabberImpl.GOOGLE_VOICE_DOMAIN);
addCallPeer(callPeer);
callPeer.setState(CallPeerState.INITIATING_CALL);
// if this was the first peer we added in this call then the call is
// new and we also need to notify everyone of its creation.
if(getCallPeerCount() == 1 && !calleeJID.endsWith(
ProtocolProviderServiceJabberImpl.GOOGLE_VOICE_DOMAIN) ||
getCallPeerCount() == 2 && firstCallPeerIsGV)
{
if(firstCallPeerIsGV)
{
// now all is setup, considered that there is no GV call
firstCallPeerIsGV = false;
Iterator<CallPeerGTalkImpl> it =
getCallPeersVector().iterator();
String sub = calleeJID.substring(0, calleeJID.indexOf("/"));
// remove Google Voice first call from CallPeer vector otherwise
// we will display a conference call window
while(it.hasNext())
{
CallPeer p = it.next();
if(p.getAddress().equals(sub))
{
it.remove();
break;
}
}
}
// if this was the first peer we added in this call then the call is
// new and we also need to notify everyone of its creation.
if(getCallPeerCount() == 1 && getCallGroup() == null)
{
parentOpSet.fireCallEvent(CallEvent.CALL_INITIATED, this);
}
else if(getCallGroup() != null)
{
// only TelephonyConferencing OperationSet should know about it
CallEvent cEvent = new CallEvent(this,
CallEvent.CALL_INITIATED);
AbstractOperationSetTelephonyConferencing<?,?,?,?,?> opSet =
(AbstractOperationSetTelephonyConferencing<?,?,?,?,?>)
getProtocolProvider().getOperationSet(
OperationSetTelephonyConferencing.class);
if(opSet != null)
opSet.outgoingCallCreated(cEvent);
}
}
CallPeerMediaHandlerGTalkImpl mediaHandler
= callPeer.getMediaHandler();
/* enable video if it is a videocall */
mediaHandler.setLocalVideoTransmissionEnabled(localVideoAllowed);
//set call state to connecting so that the user interface would start
//playing the tones. we do that here because we may be harvesting
//STUN/TURN addresses in initiateSession() which would take a while.
callPeer.setState(CallPeerState.CONNECTING);
// if initializing session fails, set peer to failed
boolean sessionInitiated = false;
try
{
callPeer.initiateSession(sessionInitiateExtensions);
sessionInitiated = true;
return callPeer;
}
finally
{
// if initialization throws an exception
if(!sessionInitiated)
{
callPeer.setState(CallPeerState.FAILED);
}
}
}
}
|