dom/telephony/TelephonyCall.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:77ab9224642a
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 }

mercurial