|
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=40: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "TelephonyCall.h" |
|
8 #include "mozilla/dom/TelephonyCallBinding.h" |
|
9 |
|
10 #include "mozilla/dom/DOMError.h" |
|
11 |
|
12 #include "CallEvent.h" |
|
13 #include "Telephony.h" |
|
14 #include "TelephonyCallGroup.h" |
|
15 |
|
16 using namespace mozilla::dom; |
|
17 using mozilla::ErrorResult; |
|
18 using mozilla::dom::telephony::kOutgoingPlaceholderCallIndex; |
|
19 |
|
20 // static |
|
21 already_AddRefed<TelephonyCall> |
|
22 TelephonyCall::Create(Telephony* aTelephony, uint32_t aServiceId, |
|
23 const nsAString& aNumber, uint16_t aCallState, |
|
24 uint32_t aCallIndex, bool aEmergency, bool aIsConference, |
|
25 bool aSwitchable, bool aMergeable) |
|
26 { |
|
27 NS_ASSERTION(aTelephony, "Null pointer!"); |
|
28 NS_ASSERTION(!aNumber.IsEmpty(), "Empty number!"); |
|
29 NS_ASSERTION(aCallIndex >= 1, "Invalid call index!"); |
|
30 |
|
31 nsRefPtr<TelephonyCall> call = new TelephonyCall(aTelephony->GetOwner()); |
|
32 |
|
33 call->mTelephony = aTelephony; |
|
34 call->mServiceId = aServiceId; |
|
35 call->mNumber = aNumber; |
|
36 call->mCallIndex = aCallIndex; |
|
37 call->mError = nullptr; |
|
38 call->mEmergency = aEmergency; |
|
39 call->mGroup = aIsConference ? aTelephony->ConferenceGroup() : nullptr; |
|
40 call->mSwitchable = aSwitchable; |
|
41 call->mMergeable = aMergeable; |
|
42 |
|
43 call->ChangeStateInternal(aCallState, false); |
|
44 |
|
45 return call.forget(); |
|
46 } |
|
47 |
|
48 TelephonyCall::TelephonyCall(nsPIDOMWindow* aOwner) |
|
49 : DOMEventTargetHelper(aOwner), |
|
50 mCallIndex(kOutgoingPlaceholderCallIndex), |
|
51 mCallState(nsITelephonyProvider::CALL_STATE_UNKNOWN), |
|
52 mLive(false) |
|
53 { |
|
54 } |
|
55 |
|
56 TelephonyCall::~TelephonyCall() |
|
57 { |
|
58 } |
|
59 |
|
60 JSObject* |
|
61 TelephonyCall::WrapObject(JSContext* aCx) |
|
62 { |
|
63 return TelephonyCallBinding::Wrap(aCx, this); |
|
64 } |
|
65 |
|
66 void |
|
67 TelephonyCall::ChangeStateInternal(uint16_t aCallState, bool aFireEvents) |
|
68 { |
|
69 nsRefPtr<TelephonyCall> kungFuDeathGrip(this); |
|
70 |
|
71 nsString stateString; |
|
72 switch (aCallState) { |
|
73 case nsITelephonyProvider::CALL_STATE_DIALING: |
|
74 stateString.AssignLiteral("dialing"); |
|
75 break; |
|
76 case nsITelephonyProvider::CALL_STATE_ALERTING: |
|
77 stateString.AssignLiteral("alerting"); |
|
78 break; |
|
79 case nsITelephonyProvider::CALL_STATE_CONNECTING: |
|
80 stateString.AssignLiteral("connecting"); |
|
81 break; |
|
82 case nsITelephonyProvider::CALL_STATE_CONNECTED: |
|
83 stateString.AssignLiteral("connected"); |
|
84 break; |
|
85 case nsITelephonyProvider::CALL_STATE_HOLDING: |
|
86 stateString.AssignLiteral("holding"); |
|
87 break; |
|
88 case nsITelephonyProvider::CALL_STATE_HELD: |
|
89 stateString.AssignLiteral("held"); |
|
90 break; |
|
91 case nsITelephonyProvider::CALL_STATE_RESUMING: |
|
92 stateString.AssignLiteral("resuming"); |
|
93 break; |
|
94 case nsITelephonyProvider::CALL_STATE_DISCONNECTING: |
|
95 stateString.AssignLiteral("disconnecting"); |
|
96 break; |
|
97 case nsITelephonyProvider::CALL_STATE_DISCONNECTED: |
|
98 stateString.AssignLiteral("disconnected"); |
|
99 break; |
|
100 case nsITelephonyProvider::CALL_STATE_INCOMING: |
|
101 stateString.AssignLiteral("incoming"); |
|
102 break; |
|
103 default: |
|
104 NS_NOTREACHED("Unknown state!"); |
|
105 } |
|
106 |
|
107 mState = stateString; |
|
108 mCallState = aCallState; |
|
109 |
|
110 if (aCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) { |
|
111 NS_ASSERTION(mLive, "Should be live!"); |
|
112 mLive = false; |
|
113 if (mGroup) { |
|
114 mGroup->RemoveCall(this); |
|
115 } else { |
|
116 mTelephony->RemoveCall(this); |
|
117 } |
|
118 } else if (!mLive) { |
|
119 mLive = true; |
|
120 if (mGroup) { |
|
121 mGroup->AddCall(this); |
|
122 } else { |
|
123 mTelephony->AddCall(this); |
|
124 } |
|
125 } |
|
126 |
|
127 if (aFireEvents) { |
|
128 nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("statechange"), this); |
|
129 if (NS_FAILED(rv)) { |
|
130 NS_WARNING("Failed to dispatch specific event!"); |
|
131 } |
|
132 |
|
133 // This can change if the statechange handler called back here... Need to |
|
134 // figure out something smarter. |
|
135 if (mCallState == aCallState) { |
|
136 rv = DispatchCallEvent(stateString, this); |
|
137 if (NS_FAILED(rv)) { |
|
138 NS_WARNING("Failed to dispatch specific event!"); |
|
139 } |
|
140 } |
|
141 } |
|
142 } |
|
143 |
|
144 nsresult |
|
145 TelephonyCall::DispatchCallEvent(const nsAString& aType, |
|
146 TelephonyCall* aCall) |
|
147 { |
|
148 MOZ_ASSERT(aCall); |
|
149 |
|
150 nsRefPtr<CallEvent> event = CallEvent::Create(this, aType, aCall, false, false); |
|
151 |
|
152 return DispatchTrustedEvent(event); |
|
153 } |
|
154 |
|
155 void |
|
156 TelephonyCall::NotifyError(const nsAString& aError) |
|
157 { |
|
158 // Set the error string |
|
159 NS_ASSERTION(!mError, "Already have an error?"); |
|
160 |
|
161 mError = new DOMError(GetOwner(), aError); |
|
162 |
|
163 // Do the state transitions |
|
164 ChangeStateInternal(nsITelephonyProvider::CALL_STATE_DISCONNECTED, true); |
|
165 |
|
166 nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("error"), this); |
|
167 if (NS_FAILED(rv)) { |
|
168 NS_WARNING("Failed to dispatch error event!"); |
|
169 } |
|
170 } |
|
171 |
|
172 void |
|
173 TelephonyCall::ChangeGroup(TelephonyCallGroup* aGroup) |
|
174 { |
|
175 mGroup = aGroup; |
|
176 |
|
177 nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("groupchange"), this); |
|
178 if (NS_FAILED(rv)) { |
|
179 NS_WARNING("Failed to dispatch error event!"); |
|
180 } |
|
181 } |
|
182 |
|
183 NS_IMPL_CYCLE_COLLECTION_INHERITED(TelephonyCall, |
|
184 DOMEventTargetHelper, |
|
185 mTelephony, |
|
186 mError, |
|
187 mGroup); |
|
188 |
|
189 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TelephonyCall) |
|
190 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) |
|
191 |
|
192 NS_IMPL_ADDREF_INHERITED(TelephonyCall, DOMEventTargetHelper) |
|
193 NS_IMPL_RELEASE_INHERITED(TelephonyCall, DOMEventTargetHelper) |
|
194 |
|
195 // TelephonyCall WebIDL |
|
196 |
|
197 already_AddRefed<DOMError> |
|
198 TelephonyCall::GetError() const |
|
199 { |
|
200 nsRefPtr<DOMError> error = mError; |
|
201 return error.forget(); |
|
202 } |
|
203 |
|
204 already_AddRefed<TelephonyCallGroup> |
|
205 TelephonyCall::GetGroup() const |
|
206 { |
|
207 nsRefPtr<TelephonyCallGroup> group = mGroup; |
|
208 return group.forget(); |
|
209 } |
|
210 |
|
211 void |
|
212 TelephonyCall::Answer(ErrorResult& aRv) |
|
213 { |
|
214 if (mCallState != nsITelephonyProvider::CALL_STATE_INCOMING) { |
|
215 NS_WARNING("Answer on non-incoming call ignored!"); |
|
216 return; |
|
217 } |
|
218 |
|
219 nsresult rv = mTelephony->Provider()->AnswerCall(mServiceId, mCallIndex); |
|
220 if (NS_FAILED(rv)) { |
|
221 aRv.Throw(rv); |
|
222 return; |
|
223 } |
|
224 |
|
225 ChangeStateInternal(nsITelephonyProvider::CALL_STATE_CONNECTING, true); |
|
226 } |
|
227 |
|
228 void |
|
229 TelephonyCall::HangUp(ErrorResult& aRv) |
|
230 { |
|
231 if (mCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTING || |
|
232 mCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) { |
|
233 NS_WARNING("HangUp on previously disconnected call ignored!"); |
|
234 return; |
|
235 } |
|
236 |
|
237 nsresult rv = mCallState == nsITelephonyProvider::CALL_STATE_INCOMING ? |
|
238 mTelephony->Provider()->RejectCall(mServiceId, mCallIndex) : |
|
239 mTelephony->Provider()->HangUp(mServiceId, mCallIndex); |
|
240 if (NS_FAILED(rv)) { |
|
241 aRv.Throw(rv); |
|
242 return; |
|
243 } |
|
244 |
|
245 ChangeStateInternal(nsITelephonyProvider::CALL_STATE_DISCONNECTING, true); |
|
246 } |
|
247 |
|
248 void |
|
249 TelephonyCall::Hold(ErrorResult& aRv) |
|
250 { |
|
251 if (mCallState != nsITelephonyProvider::CALL_STATE_CONNECTED) { |
|
252 NS_WARNING("Hold non-connected call ignored!"); |
|
253 return; |
|
254 } |
|
255 |
|
256 if (mGroup) { |
|
257 NS_WARNING("Hold a call in conference ignored!"); |
|
258 return; |
|
259 } |
|
260 |
|
261 if (!mSwitchable) { |
|
262 NS_WARNING("Hold a non-switchable call ignored!"); |
|
263 return; |
|
264 } |
|
265 |
|
266 nsresult rv = mTelephony->Provider()->HoldCall(mServiceId, mCallIndex); |
|
267 if (NS_FAILED(rv)) { |
|
268 aRv.Throw(rv); |
|
269 return; |
|
270 } |
|
271 |
|
272 if (!mSecondNumber.IsEmpty()) { |
|
273 // No state transition when we switch two numbers within one TelephonyCall |
|
274 // object. Otherwise, the state here will be inconsistent with the backend |
|
275 // RIL and will never be right. |
|
276 return; |
|
277 } |
|
278 |
|
279 ChangeStateInternal(nsITelephonyProvider::CALL_STATE_HOLDING, true); |
|
280 } |
|
281 |
|
282 void |
|
283 TelephonyCall::Resume(ErrorResult& aRv) |
|
284 { |
|
285 if (mCallState != nsITelephonyProvider::CALL_STATE_HELD) { |
|
286 NS_WARNING("Resume non-held call ignored!"); |
|
287 return; |
|
288 } |
|
289 |
|
290 if (mGroup) { |
|
291 NS_WARNING("Resume a call in conference ignored!"); |
|
292 return; |
|
293 } |
|
294 |
|
295 if (!mSwitchable) { |
|
296 NS_WARNING("Resume a non-switchable call ignored!"); |
|
297 return; |
|
298 } |
|
299 |
|
300 nsresult rv = mTelephony->Provider()->ResumeCall(mServiceId, mCallIndex); |
|
301 if (NS_FAILED(rv)) { |
|
302 aRv.Throw(rv); |
|
303 return; |
|
304 } |
|
305 |
|
306 ChangeStateInternal(nsITelephonyProvider::CALL_STATE_RESUMING, true); |
|
307 } |