|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
|
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 "Telephony.h" |
|
8 #include "mozilla/dom/TelephonyBinding.h" |
|
9 #include "mozilla/dom/Promise.h" |
|
10 |
|
11 #include "nsIURI.h" |
|
12 #include "nsPIDOMWindow.h" |
|
13 #include "nsIPermissionManager.h" |
|
14 |
|
15 #include "mozilla/dom/UnionTypes.h" |
|
16 #include "mozilla/Preferences.h" |
|
17 #include "nsCharSeparatedTokenizer.h" |
|
18 #include "nsContentUtils.h" |
|
19 #include "nsCxPusher.h" |
|
20 #include "nsNetUtil.h" |
|
21 #include "nsServiceManagerUtils.h" |
|
22 #include "nsThreadUtils.h" |
|
23 |
|
24 #include "CallEvent.h" |
|
25 #include "CallsList.h" |
|
26 #include "TelephonyCall.h" |
|
27 #include "TelephonyCallGroup.h" |
|
28 |
|
29 using namespace mozilla::dom; |
|
30 using mozilla::ErrorResult; |
|
31 using mozilla::dom::telephony::kOutgoingPlaceholderCallIndex; |
|
32 |
|
33 class Telephony::Listener : public nsITelephonyListener |
|
34 { |
|
35 Telephony* mTelephony; |
|
36 |
|
37 public: |
|
38 NS_DECL_ISUPPORTS |
|
39 NS_FORWARD_SAFE_NSITELEPHONYLISTENER(mTelephony) |
|
40 |
|
41 Listener(Telephony* aTelephony) |
|
42 : mTelephony(aTelephony) |
|
43 { |
|
44 MOZ_ASSERT(mTelephony); |
|
45 } |
|
46 |
|
47 virtual ~Listener() {} |
|
48 |
|
49 void |
|
50 Disconnect() |
|
51 { |
|
52 MOZ_ASSERT(mTelephony); |
|
53 mTelephony = nullptr; |
|
54 } |
|
55 }; |
|
56 |
|
57 class Telephony::Callback : public nsITelephonyCallback |
|
58 { |
|
59 nsRefPtr<Telephony> mTelephony; |
|
60 nsRefPtr<Promise> mPromise; |
|
61 uint32_t mServiceId; |
|
62 nsString mNumber; |
|
63 |
|
64 public: |
|
65 NS_DECL_ISUPPORTS |
|
66 |
|
67 Callback(Telephony* aTelephony, Promise* aPromise, uint32_t aServiceId, |
|
68 const nsAString& aNumber) |
|
69 : mTelephony(aTelephony), mPromise(aPromise), mServiceId(aServiceId), |
|
70 mNumber(aNumber) |
|
71 { |
|
72 MOZ_ASSERT(mTelephony); |
|
73 } |
|
74 |
|
75 virtual ~Callback() {} |
|
76 |
|
77 NS_IMETHODIMP |
|
78 NotifyDialError(const nsAString& aError) |
|
79 { |
|
80 mPromise->MaybeReject(aError); |
|
81 return NS_OK; |
|
82 } |
|
83 |
|
84 NS_IMETHODIMP |
|
85 NotifyDialSuccess() |
|
86 { |
|
87 nsRefPtr<TelephonyCall> call = |
|
88 mTelephony->CreateNewDialingCall(mServiceId, mNumber); |
|
89 |
|
90 mPromise->MaybeResolve(call); |
|
91 return NS_OK; |
|
92 } |
|
93 }; |
|
94 |
|
95 class Telephony::EnumerationAck : public nsRunnable |
|
96 { |
|
97 nsRefPtr<Telephony> mTelephony; |
|
98 |
|
99 public: |
|
100 EnumerationAck(Telephony* aTelephony) |
|
101 : mTelephony(aTelephony) |
|
102 { |
|
103 MOZ_ASSERT(mTelephony); |
|
104 } |
|
105 |
|
106 NS_IMETHOD Run() |
|
107 { |
|
108 mTelephony->NotifyCallsChanged(nullptr); |
|
109 return NS_OK; |
|
110 } |
|
111 }; |
|
112 |
|
113 Telephony::Telephony(nsPIDOMWindow* aOwner) |
|
114 : DOMEventTargetHelper(aOwner), mActiveCall(nullptr), mEnumerated(false) |
|
115 { |
|
116 } |
|
117 |
|
118 Telephony::~Telephony() |
|
119 { |
|
120 Shutdown(); |
|
121 } |
|
122 |
|
123 void |
|
124 Telephony::Shutdown() |
|
125 { |
|
126 if (mListener) { |
|
127 mListener->Disconnect(); |
|
128 |
|
129 if (mProvider) { |
|
130 mProvider->UnregisterListener(mListener); |
|
131 mProvider = nullptr; |
|
132 } |
|
133 |
|
134 mListener = nullptr; |
|
135 } |
|
136 } |
|
137 |
|
138 JSObject* |
|
139 Telephony::WrapObject(JSContext* aCx) |
|
140 { |
|
141 return TelephonyBinding::Wrap(aCx, this); |
|
142 } |
|
143 |
|
144 // static |
|
145 already_AddRefed<Telephony> |
|
146 Telephony::Create(nsPIDOMWindow* aOwner, ErrorResult& aRv) |
|
147 { |
|
148 NS_ASSERTION(aOwner, "Null owner!"); |
|
149 |
|
150 nsCOMPtr<nsITelephonyProvider> ril = |
|
151 do_GetService(TELEPHONY_PROVIDER_CONTRACTID); |
|
152 if (!ril) { |
|
153 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
154 return nullptr; |
|
155 } |
|
156 |
|
157 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner); |
|
158 if (!sgo) { |
|
159 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
160 return nullptr; |
|
161 } |
|
162 |
|
163 nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext(); |
|
164 if (!scriptContext) { |
|
165 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
166 return nullptr; |
|
167 } |
|
168 |
|
169 nsRefPtr<Telephony> telephony = new Telephony(aOwner); |
|
170 |
|
171 telephony->mProvider = ril; |
|
172 telephony->mListener = new Listener(telephony); |
|
173 telephony->mCallsList = new CallsList(telephony); |
|
174 telephony->mGroup = TelephonyCallGroup::Create(telephony); |
|
175 |
|
176 nsresult rv = ril->EnumerateCalls(telephony->mListener); |
|
177 if (NS_FAILED(rv)) { |
|
178 aRv.Throw(rv); |
|
179 return nullptr; |
|
180 } |
|
181 |
|
182 return telephony.forget(); |
|
183 } |
|
184 |
|
185 // static |
|
186 bool |
|
187 Telephony::IsValidNumber(const nsAString& aNumber) |
|
188 { |
|
189 return !aNumber.IsEmpty(); |
|
190 } |
|
191 |
|
192 // static |
|
193 uint32_t |
|
194 Telephony::GetNumServices() { |
|
195 return mozilla::Preferences::GetInt("ril.numRadioInterfaces", 1); |
|
196 } |
|
197 |
|
198 // static |
|
199 bool |
|
200 Telephony::IsValidServiceId(uint32_t aServiceId) |
|
201 { |
|
202 return aServiceId < GetNumServices(); |
|
203 } |
|
204 |
|
205 // static |
|
206 bool |
|
207 Telephony::IsActiveState(uint16_t aCallState) { |
|
208 return aCallState == nsITelephonyProvider::CALL_STATE_DIALING || |
|
209 aCallState == nsITelephonyProvider::CALL_STATE_ALERTING || |
|
210 aCallState == nsITelephonyProvider::CALL_STATE_CONNECTED; |
|
211 } |
|
212 |
|
213 uint32_t |
|
214 Telephony::ProvidedOrDefaultServiceId(const Optional<uint32_t>& aServiceId) |
|
215 { |
|
216 if (aServiceId.WasPassed()) { |
|
217 return aServiceId.Value(); |
|
218 } else { |
|
219 uint32_t serviceId = 0; |
|
220 mProvider->GetDefaultServiceId(&serviceId); |
|
221 return serviceId; |
|
222 } |
|
223 } |
|
224 |
|
225 bool |
|
226 Telephony::HasDialingCall() |
|
227 { |
|
228 for (uint32_t i = 0; i < mCalls.Length(); i++) { |
|
229 const nsRefPtr<TelephonyCall>& call = mCalls[i]; |
|
230 if (call->CallState() > nsITelephonyProvider::CALL_STATE_UNKNOWN && |
|
231 call->CallState() < nsITelephonyProvider::CALL_STATE_CONNECTED) { |
|
232 return true; |
|
233 } |
|
234 } |
|
235 |
|
236 return false; |
|
237 } |
|
238 |
|
239 bool |
|
240 Telephony::MatchActiveCall(TelephonyCall* aCall) |
|
241 { |
|
242 return (mActiveCall && |
|
243 mActiveCall->CallIndex() == aCall->CallIndex() && |
|
244 mActiveCall->ServiceId() == aCall->ServiceId()); |
|
245 } |
|
246 |
|
247 already_AddRefed<Promise> |
|
248 Telephony::DialInternal(uint32_t aServiceId, const nsAString& aNumber, |
|
249 bool aIsEmergency) |
|
250 { |
|
251 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner()); |
|
252 if (!global) { |
|
253 return nullptr; |
|
254 } |
|
255 |
|
256 nsRefPtr<Promise> promise = new Promise(global); |
|
257 |
|
258 if (!IsValidNumber(aNumber) || !IsValidServiceId(aServiceId)) { |
|
259 promise->MaybeReject(NS_LITERAL_STRING("InvalidAccessError")); |
|
260 return promise.forget(); |
|
261 } |
|
262 |
|
263 // We only support one outgoing call at a time. |
|
264 if (HasDialingCall()) { |
|
265 promise->MaybeReject(NS_LITERAL_STRING("InvalidStateError")); |
|
266 return promise.forget(); |
|
267 } |
|
268 |
|
269 nsCOMPtr<nsITelephonyCallback> callback = |
|
270 new Callback(this, promise, aServiceId, aNumber); |
|
271 nsresult rv = mProvider->Dial(aServiceId, aNumber, aIsEmergency, callback); |
|
272 if (NS_FAILED(rv)) { |
|
273 promise->MaybeReject(NS_LITERAL_STRING("InvalidStateError")); |
|
274 return promise.forget(); |
|
275 } |
|
276 |
|
277 return promise.forget(); |
|
278 } |
|
279 |
|
280 already_AddRefed<TelephonyCall> |
|
281 Telephony::CreateNewDialingCall(uint32_t aServiceId, const nsAString& aNumber) |
|
282 { |
|
283 nsRefPtr<TelephonyCall> call = |
|
284 TelephonyCall::Create(this, aServiceId, aNumber, |
|
285 nsITelephonyProvider::CALL_STATE_DIALING); |
|
286 NS_ASSERTION(call, "This should never fail!"); |
|
287 |
|
288 NS_ASSERTION(mCalls.Contains(call), "Should have auto-added new call!"); |
|
289 |
|
290 return call.forget(); |
|
291 } |
|
292 |
|
293 nsresult |
|
294 Telephony::NotifyCallsChanged(TelephonyCall* aCall) |
|
295 { |
|
296 return DispatchCallEvent(NS_LITERAL_STRING("callschanged"), aCall); |
|
297 } |
|
298 |
|
299 void |
|
300 Telephony::UpdateActiveCall(TelephonyCall* aCall, bool aIsActive) |
|
301 { |
|
302 if (aIsActive) { |
|
303 mActiveCall = aCall; |
|
304 } else if (MatchActiveCall(aCall)) { |
|
305 mActiveCall = nullptr; |
|
306 } |
|
307 } |
|
308 |
|
309 already_AddRefed<TelephonyCall> |
|
310 Telephony::GetCall(uint32_t aServiceId, uint32_t aCallIndex) |
|
311 { |
|
312 nsRefPtr<TelephonyCall> call; |
|
313 |
|
314 for (uint32_t i = 0; i < mCalls.Length(); i++) { |
|
315 nsRefPtr<TelephonyCall>& tempCall = mCalls[i]; |
|
316 if (tempCall->ServiceId() == aServiceId && |
|
317 tempCall->CallIndex() == aCallIndex) { |
|
318 call = tempCall; |
|
319 break; |
|
320 } |
|
321 } |
|
322 |
|
323 return call.forget(); |
|
324 } |
|
325 |
|
326 already_AddRefed<TelephonyCall> |
|
327 Telephony::GetOutgoingCall() |
|
328 { |
|
329 nsRefPtr<TelephonyCall> call; |
|
330 |
|
331 for (uint32_t i = 0; i < mCalls.Length(); i++) { |
|
332 nsRefPtr<TelephonyCall>& tempCall = mCalls[i]; |
|
333 if (tempCall->CallIndex() == kOutgoingPlaceholderCallIndex) { |
|
334 NS_ASSERTION(!call, "More than one outgoing call not supported!"); |
|
335 NS_ASSERTION(tempCall->CallState() == nsITelephonyProvider::CALL_STATE_DIALING, |
|
336 "Something really wrong here!"); |
|
337 |
|
338 call = tempCall; |
|
339 // No break. We will search entire list to ensure only one outgoing call. |
|
340 } |
|
341 } |
|
342 |
|
343 return call.forget(); |
|
344 } |
|
345 |
|
346 already_AddRefed<TelephonyCall> |
|
347 Telephony::GetCallFromEverywhere(uint32_t aServiceId, uint32_t aCallIndex) |
|
348 { |
|
349 nsRefPtr<TelephonyCall> call = GetCall(aServiceId, aCallIndex); |
|
350 |
|
351 if (!call) { |
|
352 call = mGroup->GetCall(aServiceId, aCallIndex); |
|
353 } |
|
354 |
|
355 return call.forget(); |
|
356 } |
|
357 |
|
358 NS_IMPL_CYCLE_COLLECTION_CLASS(Telephony) |
|
359 |
|
360 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Telephony, |
|
361 DOMEventTargetHelper) |
|
362 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCalls) |
|
363 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallsList) |
|
364 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGroup) |
|
365 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
366 |
|
367 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Telephony, |
|
368 DOMEventTargetHelper) |
|
369 tmp->Shutdown(); |
|
370 tmp->mActiveCall = nullptr; |
|
371 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCalls) |
|
372 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallsList) |
|
373 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGroup) |
|
374 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
375 |
|
376 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Telephony) |
|
377 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) |
|
378 |
|
379 NS_IMPL_ADDREF_INHERITED(Telephony, DOMEventTargetHelper) |
|
380 NS_IMPL_RELEASE_INHERITED(Telephony, DOMEventTargetHelper) |
|
381 |
|
382 NS_IMPL_ISUPPORTS(Telephony::Listener, nsITelephonyListener) |
|
383 NS_IMPL_ISUPPORTS(Telephony::Callback, nsITelephonyCallback) |
|
384 |
|
385 // Telephony WebIDL |
|
386 |
|
387 already_AddRefed<Promise> |
|
388 Telephony::Dial(const nsAString& aNumber, const Optional<uint32_t>& aServiceId) |
|
389 { |
|
390 uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); |
|
391 nsRefPtr<Promise> promise = DialInternal(serviceId, aNumber, false); |
|
392 return promise.forget(); |
|
393 } |
|
394 |
|
395 already_AddRefed<Promise> |
|
396 Telephony::DialEmergency(const nsAString& aNumber, |
|
397 const Optional<uint32_t>& aServiceId) |
|
398 { |
|
399 uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); |
|
400 nsRefPtr<Promise> promise = DialInternal(serviceId, aNumber, true); |
|
401 return promise.forget(); |
|
402 } |
|
403 |
|
404 void |
|
405 Telephony::StartTone(const nsAString& aDTMFChar, |
|
406 const Optional<uint32_t>& aServiceId, |
|
407 ErrorResult& aRv) |
|
408 { |
|
409 uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); |
|
410 |
|
411 if (aDTMFChar.IsEmpty()) { |
|
412 NS_WARNING("Empty tone string will be ignored"); |
|
413 return; |
|
414 } |
|
415 |
|
416 if (aDTMFChar.Length() > 1 || !IsValidServiceId(serviceId)) { |
|
417 aRv.Throw(NS_ERROR_INVALID_ARG); |
|
418 return; |
|
419 } |
|
420 |
|
421 aRv = mProvider->StartTone(serviceId, aDTMFChar); |
|
422 } |
|
423 |
|
424 void |
|
425 Telephony::StopTone(const Optional<uint32_t>& aServiceId, ErrorResult& aRv) |
|
426 { |
|
427 uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); |
|
428 |
|
429 if (!IsValidServiceId(serviceId)) { |
|
430 aRv.Throw(NS_ERROR_INVALID_ARG); |
|
431 return; |
|
432 } |
|
433 |
|
434 aRv = mProvider->StopTone(serviceId); |
|
435 } |
|
436 |
|
437 bool |
|
438 Telephony::GetMuted(ErrorResult& aRv) const |
|
439 { |
|
440 bool muted = false; |
|
441 aRv = mProvider->GetMicrophoneMuted(&muted); |
|
442 |
|
443 return muted; |
|
444 } |
|
445 |
|
446 void |
|
447 Telephony::SetMuted(bool aMuted, ErrorResult& aRv) |
|
448 { |
|
449 aRv = mProvider->SetMicrophoneMuted(aMuted); |
|
450 } |
|
451 |
|
452 bool |
|
453 Telephony::GetSpeakerEnabled(ErrorResult& aRv) const |
|
454 { |
|
455 bool enabled = false; |
|
456 aRv = mProvider->GetSpeakerEnabled(&enabled); |
|
457 |
|
458 return enabled; |
|
459 } |
|
460 |
|
461 void |
|
462 Telephony::SetSpeakerEnabled(bool aEnabled, ErrorResult& aRv) |
|
463 { |
|
464 aRv = mProvider->SetSpeakerEnabled(aEnabled); |
|
465 } |
|
466 |
|
467 void |
|
468 Telephony::GetActive(Nullable<OwningTelephonyCallOrTelephonyCallGroup>& aValue) |
|
469 { |
|
470 if (mActiveCall) { |
|
471 aValue.SetValue().SetAsTelephonyCall() = mActiveCall; |
|
472 } else if (mGroup->CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED) { |
|
473 aValue.SetValue().SetAsTelephonyCallGroup() = mGroup; |
|
474 } else { |
|
475 aValue.SetNull(); |
|
476 } |
|
477 } |
|
478 |
|
479 already_AddRefed<CallsList> |
|
480 Telephony::Calls() const |
|
481 { |
|
482 nsRefPtr<CallsList> list = mCallsList; |
|
483 return list.forget(); |
|
484 } |
|
485 |
|
486 already_AddRefed<TelephonyCallGroup> |
|
487 Telephony::ConferenceGroup() const |
|
488 { |
|
489 nsRefPtr<TelephonyCallGroup> group = mGroup; |
|
490 return group.forget(); |
|
491 } |
|
492 |
|
493 // EventTarget |
|
494 |
|
495 void |
|
496 Telephony::EventListenerAdded(nsIAtom* aType) |
|
497 { |
|
498 if (aType == nsGkAtoms::oncallschanged) { |
|
499 // Fire oncallschanged on the next tick if the calls array is ready. |
|
500 EnqueueEnumerationAck(); |
|
501 } |
|
502 } |
|
503 |
|
504 // nsITelephonyListener |
|
505 |
|
506 NS_IMETHODIMP |
|
507 Telephony::CallStateChanged(uint32_t aServiceId, uint32_t aCallIndex, |
|
508 uint16_t aCallState, const nsAString& aNumber, |
|
509 bool aIsActive, bool aIsOutgoing, bool aIsEmergency, |
|
510 bool aIsConference, bool aIsSwitchable, bool aIsMergeable) |
|
511 { |
|
512 nsRefPtr<TelephonyCall> modifiedCall |
|
513 = GetCallFromEverywhere(aServiceId, aCallIndex); |
|
514 |
|
515 // Try to use the outgoing call if we don't find the modified call. |
|
516 if (!modifiedCall) { |
|
517 nsRefPtr<TelephonyCall> outgoingCall = GetOutgoingCall(); |
|
518 |
|
519 // If the call state isn't incoming but we do have an outgoing call then |
|
520 // we must be seeing a status update for our outgoing call. |
|
521 if (outgoingCall && |
|
522 aCallState != nsITelephonyProvider::CALL_STATE_INCOMING) { |
|
523 outgoingCall->UpdateCallIndex(aCallIndex); |
|
524 outgoingCall->UpdateEmergency(aIsEmergency); |
|
525 modifiedCall.swap(outgoingCall); |
|
526 } |
|
527 } |
|
528 |
|
529 if (modifiedCall) { |
|
530 modifiedCall->UpdateSwitchable(aIsSwitchable); |
|
531 modifiedCall->UpdateMergeable(aIsMergeable); |
|
532 |
|
533 if (!aIsConference) { |
|
534 UpdateActiveCall(modifiedCall, aIsActive); |
|
535 } |
|
536 |
|
537 if (modifiedCall->CallState() != aCallState) { |
|
538 // We don't fire the statechange event on a call in conference here. |
|
539 // Instead, the event will be fired later in |
|
540 // TelephonyCallGroup::ChangeState(). Thus the sequence of firing the |
|
541 // statechange events is guaranteed: first on TelephonyCallGroup then on |
|
542 // individual TelephonyCall objects. |
|
543 bool fireEvent = !aIsConference; |
|
544 modifiedCall->ChangeStateInternal(aCallState, fireEvent); |
|
545 } |
|
546 |
|
547 nsRefPtr<TelephonyCallGroup> group = modifiedCall->GetGroup(); |
|
548 |
|
549 if (!group && aIsConference) { |
|
550 // Add to conference. |
|
551 NS_ASSERTION(mCalls.Contains(modifiedCall), "Should in mCalls"); |
|
552 mGroup->AddCall(modifiedCall); |
|
553 RemoveCall(modifiedCall); |
|
554 } else if (group && !aIsConference) { |
|
555 // Remove from conference. |
|
556 NS_ASSERTION(mGroup->CallsArray().Contains(modifiedCall), "Should in mGroup"); |
|
557 mGroup->RemoveCall(modifiedCall); |
|
558 AddCall(modifiedCall); |
|
559 } |
|
560 |
|
561 return NS_OK; |
|
562 } |
|
563 |
|
564 // Do nothing since we didn't know anything about it before now and it's |
|
565 // ended already. |
|
566 if (aCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) { |
|
567 return NS_OK; |
|
568 } |
|
569 |
|
570 // Didn't find this call in mCalls or mGroup. Create a new call. |
|
571 nsRefPtr<TelephonyCall> call = |
|
572 TelephonyCall::Create(this, aServiceId, aNumber, aCallState, aCallIndex, |
|
573 aIsEmergency, aIsConference, aIsSwitchable, |
|
574 aIsMergeable); |
|
575 NS_ASSERTION(call, "This should never fail!"); |
|
576 |
|
577 NS_ASSERTION(aIsConference ? mGroup->CallsArray().Contains(call) : |
|
578 mCalls.Contains(call), |
|
579 "Should have auto-added new call!"); |
|
580 |
|
581 if (aCallState == nsITelephonyProvider::CALL_STATE_INCOMING) { |
|
582 nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("incoming"), call); |
|
583 NS_ENSURE_SUCCESS(rv, rv); |
|
584 } |
|
585 |
|
586 return NS_OK; |
|
587 } |
|
588 |
|
589 NS_IMETHODIMP |
|
590 Telephony::ConferenceCallStateChanged(uint16_t aCallState) |
|
591 { |
|
592 mGroup->ChangeState(aCallState); |
|
593 return NS_OK; |
|
594 } |
|
595 |
|
596 NS_IMETHODIMP |
|
597 Telephony::EnumerateCallStateComplete() |
|
598 { |
|
599 MOZ_ASSERT(!mEnumerated); |
|
600 |
|
601 mEnumerated = true; |
|
602 |
|
603 if (NS_FAILED(NotifyCallsChanged(nullptr))) { |
|
604 NS_WARNING("Failed to notify calls changed!"); |
|
605 } |
|
606 |
|
607 if (NS_FAILED(mProvider->RegisterListener(mListener))) { |
|
608 NS_WARNING("Failed to register listener!"); |
|
609 } |
|
610 return NS_OK; |
|
611 } |
|
612 |
|
613 NS_IMETHODIMP |
|
614 Telephony::EnumerateCallState(uint32_t aServiceId, uint32_t aCallIndex, |
|
615 uint16_t aCallState, const nsAString& aNumber, |
|
616 bool aIsActive, bool aIsOutgoing, bool aIsEmergency, |
|
617 bool aIsConference, bool aIsSwitchable, bool aIsMergeable) |
|
618 { |
|
619 nsRefPtr<TelephonyCall> call; |
|
620 |
|
621 // We request calls enumeration in constructor, and the asynchronous result |
|
622 // will be sent back through the callback function EnumerateCallState(). |
|
623 // However, it is likely to have call state changes, i.e. CallStateChanged() |
|
624 // being called, before the enumeration result comes back. We'd make sure |
|
625 // we don't somehow add duplicates due to the race condition. |
|
626 call = GetCallFromEverywhere(aServiceId, aCallIndex); |
|
627 if (call) { |
|
628 return NS_OK; |
|
629 } |
|
630 |
|
631 // Didn't know anything about this call before now. |
|
632 call = TelephonyCall::Create(this, aServiceId, aNumber, aCallState, |
|
633 aCallIndex, aIsEmergency, aIsConference, |
|
634 aIsSwitchable, aIsMergeable); |
|
635 NS_ASSERTION(call, "This should never fail!"); |
|
636 |
|
637 NS_ASSERTION(aIsConference ? mGroup->CallsArray().Contains(call) : |
|
638 mCalls.Contains(call), |
|
639 "Should have auto-added new call!"); |
|
640 |
|
641 return NS_OK; |
|
642 } |
|
643 |
|
644 NS_IMETHODIMP |
|
645 Telephony::SupplementaryServiceNotification(uint32_t aServiceId, |
|
646 int32_t aCallIndex, |
|
647 uint16_t aNotification) |
|
648 { |
|
649 nsRefPtr<TelephonyCall> associatedCall; |
|
650 if (!mCalls.IsEmpty()) { |
|
651 associatedCall = GetCall(aServiceId, aCallIndex); |
|
652 } |
|
653 |
|
654 nsresult rv; |
|
655 switch (aNotification) { |
|
656 case nsITelephonyProvider::NOTIFICATION_REMOTE_HELD: |
|
657 rv = DispatchCallEvent(NS_LITERAL_STRING("remoteheld"), associatedCall); |
|
658 break; |
|
659 case nsITelephonyProvider::NOTIFICATION_REMOTE_RESUMED: |
|
660 rv = DispatchCallEvent(NS_LITERAL_STRING("remoteresumed"), associatedCall); |
|
661 break; |
|
662 default: |
|
663 NS_ERROR("Got a bad notification!"); |
|
664 return NS_ERROR_UNEXPECTED; |
|
665 } |
|
666 |
|
667 NS_ENSURE_SUCCESS(rv, rv); |
|
668 return NS_OK; |
|
669 } |
|
670 |
|
671 NS_IMETHODIMP |
|
672 Telephony::NotifyError(uint32_t aServiceId, |
|
673 int32_t aCallIndex, |
|
674 const nsAString& aError) |
|
675 { |
|
676 if (mCalls.IsEmpty()) { |
|
677 NS_ERROR("No existing call!"); |
|
678 return NS_ERROR_UNEXPECTED; |
|
679 } |
|
680 |
|
681 nsRefPtr<TelephonyCall> callToNotify = GetCall(aServiceId, aCallIndex); |
|
682 if (!callToNotify) { |
|
683 NS_ERROR("Don't call me with a bad call index!"); |
|
684 return NS_ERROR_UNEXPECTED; |
|
685 } |
|
686 |
|
687 UpdateActiveCall(callToNotify, false); |
|
688 |
|
689 // Set the call state to 'disconnected' and remove it from the calls list. |
|
690 callToNotify->NotifyError(aError); |
|
691 |
|
692 return NS_OK; |
|
693 } |
|
694 |
|
695 NS_IMETHODIMP |
|
696 Telephony::NotifyCdmaCallWaiting(uint32_t aServiceId, const nsAString& aNumber) |
|
697 { |
|
698 MOZ_ASSERT(mCalls.Length() == 1); |
|
699 |
|
700 nsRefPtr<TelephonyCall> callToNotify = mCalls[0]; |
|
701 MOZ_ASSERT(callToNotify && callToNotify->ServiceId() == aServiceId); |
|
702 |
|
703 callToNotify->UpdateSecondNumber(aNumber); |
|
704 DispatchCallEvent(NS_LITERAL_STRING("callschanged"), callToNotify); |
|
705 return NS_OK; |
|
706 } |
|
707 |
|
708 NS_IMETHODIMP |
|
709 Telephony::NotifyConferenceError(const nsAString& aName, |
|
710 const nsAString& aMessage) |
|
711 { |
|
712 mGroup->NotifyError(aName, aMessage); |
|
713 return NS_OK; |
|
714 } |
|
715 |
|
716 nsresult |
|
717 Telephony::DispatchCallEvent(const nsAString& aType, |
|
718 TelephonyCall* aCall) |
|
719 { |
|
720 // The call may be null in following cases: |
|
721 // 1. callschanged when notifying enumeration being completed |
|
722 // 2. remoteheld/remoteresumed. |
|
723 MOZ_ASSERT(aCall || |
|
724 aType.EqualsLiteral("callschanged") || |
|
725 aType.EqualsLiteral("remoteheld") || |
|
726 aType.EqualsLiteral("remtoeresumed")); |
|
727 |
|
728 nsRefPtr<CallEvent> event = CallEvent::Create(this, aType, aCall, false, false); |
|
729 |
|
730 return DispatchTrustedEvent(event); |
|
731 } |
|
732 |
|
733 void |
|
734 Telephony::EnqueueEnumerationAck() |
|
735 { |
|
736 if (!mEnumerated) { |
|
737 return; |
|
738 } |
|
739 |
|
740 nsCOMPtr<nsIRunnable> task = new EnumerationAck(this); |
|
741 if (NS_FAILED(NS_DispatchToCurrentThread(task))) { |
|
742 NS_WARNING("Failed to dispatch to current thread!"); |
|
743 } |
|
744 } |