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/. */
11 #include "nsPIDOMWindow.h"
14 #include "nsIPermissionManager.h"
15 #include "nsCharSeparatedTokenizer.h"
16 #include "nsContentUtils.h"
17 #include "nsDOMClassInfo.h"
18 #include "nsIInterfaceRequestorUtils.h"
19 #include "nsNetUtil.h"
20 #include "nsServiceManagerUtils.h"
21 #include "SystemWorkerManager.h"
22 #include "nsRadioInterfaceLayer.h"
23 #include "nsTArrayHelpers.h"
25 #include "CallEvent.h"
26 #include "TelephonyCall.h"
28 USING_TELEPHONY_NAMESPACE
29 using namespace mozilla::dom::gonk
;
33 typedef nsAutoTArray
<Telephony
*, 2> TelephonyList
;
35 TelephonyList
* gTelephonyList
;
37 } // anonymous namespace
39 Telephony::Telephony()
40 : mActiveCall(nullptr), mCallsArray(nullptr), mRooted(false)
42 if (!gTelephonyList
) {
43 gTelephonyList
= new TelephonyList();
46 gTelephonyList
->AppendElement(this);
49 Telephony::~Telephony()
51 if (mRIL
&& mRILTelephonyCallback
) {
52 mRIL
->UnregisterTelephonyCallback(mRILTelephonyCallback
);
56 mCallsArray
= nullptr;
57 NS_DROP_JS_OBJECTS(this, Telephony
);
60 NS_ASSERTION(gTelephonyList
, "This should never be null!");
61 NS_ASSERTION(gTelephonyList
->Contains(this), "Should be in the list!");
63 if (gTelephonyList
->Length() == 1) {
64 delete gTelephonyList
;
65 gTelephonyList
= nullptr;
68 gTelephonyList
->RemoveElement(this);
73 already_AddRefed
<Telephony
>
74 Telephony::Create(nsPIDOMWindow
* aOwner
, nsIRILContentHelper
* aRIL
)
76 NS_ASSERTION(aOwner
, "Null owner!");
77 NS_ASSERTION(aRIL
, "Null RIL!");
79 nsCOMPtr
<nsIScriptGlobalObject
> sgo
= do_QueryInterface(aOwner
);
80 NS_ENSURE_TRUE(sgo
, nullptr);
82 nsCOMPtr
<nsIScriptContext
> scriptContext
= sgo
->GetContext();
83 NS_ENSURE_TRUE(scriptContext
, nullptr);
85 nsRefPtr
<Telephony
> telephony
= new Telephony();
87 telephony
->BindToOwner(aOwner
);
89 telephony
->mRIL
= aRIL
;
90 telephony
->mRILTelephonyCallback
= new RILTelephonyCallback(telephony
);
92 nsresult rv
= aRIL
->EnumerateCalls(telephony
->mRILTelephonyCallback
);
93 NS_ENSURE_SUCCESS(rv
, nullptr);
95 rv
= aRIL
->RegisterTelephonyCallback(telephony
->mRILTelephonyCallback
);
96 NS_ENSURE_SUCCESS(rv
, nullptr);
98 rv
= aRIL
->RegisterTelephonyMsg();
99 NS_ENSURE_SUCCESS(rv
, nullptr);
101 return telephony
.forget();
104 already_AddRefed
<TelephonyCall
>
105 Telephony::CreateNewDialingCall(const nsAString
& aNumber
)
107 nsRefPtr
<TelephonyCall
> call
=
108 TelephonyCall::Create(this, aNumber
,
109 nsIRadioInterfaceLayer::CALL_STATE_DIALING
);
110 NS_ASSERTION(call
, "This should never fail!");
112 NS_ASSERTION(mCalls
.Contains(call
), "Should have auto-added new call!");
114 return call
.forget();
118 Telephony::NoteDialedCallFromOtherInstance(const nsAString
& aNumber
)
120 // We don't need to hang on to this call object, it is held alive by mCalls.
121 nsRefPtr
<TelephonyCall
> call
= CreateNewDialingCall(aNumber
);
125 Telephony::NotifyCallsChanged(TelephonyCall
* aCall
)
127 nsRefPtr
<CallEvent
> event
= CallEvent::Create(aCall
);
128 NS_ASSERTION(event
, "This should never fail!");
130 if (aCall
->CallState() == nsIRadioInterfaceLayer::CALL_STATE_DIALING
||
131 aCall
->CallState() == nsIRadioInterfaceLayer::CALL_STATE_ALERTING
||
132 aCall
->CallState() == nsIRadioInterfaceLayer::CALL_STATE_CONNECTED
) {
133 NS_ASSERTION(!mActiveCall
, "Already have an active call!");
135 } else if (mActiveCall
&& mActiveCall
->CallIndex() == aCall
->CallIndex()) {
136 mActiveCall
= nullptr;
140 event
->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("callschanged"));
141 NS_ENSURE_SUCCESS(rv
, rv
);
147 Telephony::DialInternal(bool isEmergency
,
148 const nsAString
& aNumber
,
149 nsIDOMTelephonyCall
** aResult
)
151 NS_ENSURE_ARG(!aNumber
.IsEmpty());
153 for (uint32_t index
= 0; index
< mCalls
.Length(); index
++) {
154 const nsRefPtr
<TelephonyCall
>& tempCall
= mCalls
[index
];
155 if (tempCall
->IsOutgoing() &&
156 tempCall
->CallState() < nsIRadioInterfaceLayer::CALL_STATE_CONNECTED
) {
157 // One call has been dialed already and we only support one outgoing call
159 NS_WARNING("Only permitted to dial one call at a time!");
160 return NS_ERROR_NOT_AVAILABLE
;
166 rv
= mRIL
->DialEmergency(aNumber
);
168 rv
= mRIL
->Dial(aNumber
);
170 NS_ENSURE_SUCCESS(rv
, rv
);
172 nsRefPtr
<TelephonyCall
> call
= CreateNewDialingCall(aNumber
);
174 // Notify other telephony objects that we just dialed.
175 for (uint32_t index
= 0; index
< gTelephonyList
->Length(); index
++) {
176 Telephony
*& telephony
= gTelephonyList
->ElementAt(index
);
177 if (telephony
!= this) {
178 nsRefPtr
<Telephony
> kungFuDeathGrip
= telephony
;
179 telephony
->NoteDialedCallFromOtherInstance(aNumber
);
183 call
.forget(aResult
);
187 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Telephony
,
188 nsDOMEventTargetHelper
)
189 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
190 for (uint32_t index
= 0; index
< tmp
->mCalls
.Length(); index
++) {
191 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mCalls[i]");
192 cb
.NoteXPCOMChild(tmp
->mCalls
[index
]->ToISupports());
194 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
196 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Telephony
,
197 nsDOMEventTargetHelper
)
198 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCallsArray
)
199 NS_IMPL_CYCLE_COLLECTION_TRACE_END
201 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Telephony
,
202 nsDOMEventTargetHelper
)
204 tmp
->mActiveCall
= nullptr;
205 tmp
->mCallsArray
= nullptr;
206 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
208 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Telephony
)
209 NS_INTERFACE_MAP_ENTRY(nsIDOMTelephony
)
210 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Telephony
)
211 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper
)
213 NS_IMPL_ADDREF_INHERITED(Telephony
, nsDOMEventTargetHelper
)
214 NS_IMPL_RELEASE_INHERITED(Telephony
, nsDOMEventTargetHelper
)
216 DOMCI_DATA(Telephony
, Telephony
)
218 NS_IMPL_ISUPPORTS1(Telephony::RILTelephonyCallback
, nsIRILTelephonyCallback
)
221 Telephony::Dial(const nsAString
& aNumber
, nsIDOMTelephonyCall
** aResult
)
223 DialInternal(false, aNumber
, aResult
);
229 Telephony::DialEmergency(const nsAString
& aNumber
, nsIDOMTelephonyCall
** aResult
)
231 DialInternal(true, aNumber
, aResult
);
237 Telephony::GetMuted(bool* aMuted
)
239 nsresult rv
= mRIL
->GetMicrophoneMuted(aMuted
);
240 NS_ENSURE_SUCCESS(rv
, rv
);
246 Telephony::SetMuted(bool aMuted
)
248 nsresult rv
= mRIL
->SetMicrophoneMuted(aMuted
);
249 NS_ENSURE_SUCCESS(rv
, rv
);
255 Telephony::GetSpeakerEnabled(bool* aSpeakerEnabled
)
257 nsresult rv
= mRIL
->GetSpeakerEnabled(aSpeakerEnabled
);
258 NS_ENSURE_SUCCESS(rv
, rv
);
264 Telephony::SetSpeakerEnabled(bool aSpeakerEnabled
)
266 nsresult rv
= mRIL
->SetSpeakerEnabled(aSpeakerEnabled
);
267 NS_ENSURE_SUCCESS(rv
, rv
);
273 Telephony::GetActive(jsval
* aActive
)
281 nsIScriptContext
* sc
= GetContextForEventHandlers(&rv
);
282 NS_ENSURE_SUCCESS(rv
, rv
);
285 nsContentUtils::WrapNative(sc
->GetNativeContext(),
286 sc
->GetNativeGlobal(),
287 mActiveCall
->ToISupports(), aActive
);
288 NS_ENSURE_SUCCESS(rv
, rv
);
294 Telephony::GetCalls(jsval
* aCalls
)
296 JSObject
* calls
= mCallsArray
;
299 nsIScriptContext
* sc
= GetContextForEventHandlers(&rv
);
300 NS_ENSURE_SUCCESS(rv
, rv
);
302 rv
= nsTArrayToJSArray(sc
->GetNativeContext(), mCalls
, &calls
);
303 NS_ENSURE_SUCCESS(rv
, rv
);
306 NS_HOLD_JS_OBJECTS(this, Telephony
);
312 NS_ENSURE_SUCCESS(rv
, rv
);
316 aCalls
->setObject(*calls
);
321 Telephony::StartTone(const nsAString
& aDTMFChar
)
323 if (aDTMFChar
.IsEmpty()) {
324 NS_WARNING("Empty tone string will be ignored");
328 if (aDTMFChar
.Length() > 1) {
329 return NS_ERROR_INVALID_ARG
;
332 nsresult rv
= mRIL
->StartTone(aDTMFChar
);
333 NS_ENSURE_SUCCESS(rv
, rv
);
339 Telephony::StopTone()
341 nsresult rv
= mRIL
->StopTone();
342 NS_ENSURE_SUCCESS(rv
, rv
);
347 NS_IMPL_EVENT_HANDLER(Telephony
, incoming
)
348 NS_IMPL_EVENT_HANDLER(Telephony
, callschanged
)
351 Telephony::CallStateChanged(uint32_t aCallIndex
, uint16_t aCallState
,
352 const nsAString
& aNumber
, bool aIsActive
)
354 NS_ASSERTION(aCallIndex
!= kOutgoingPlaceholderCallIndex
,
355 "This should never happen!");
357 nsRefPtr
<TelephonyCall
> modifiedCall
;
358 nsRefPtr
<TelephonyCall
> outgoingCall
;
360 for (uint32_t index
= 0; index
< mCalls
.Length(); index
++) {
361 nsRefPtr
<TelephonyCall
>& tempCall
= mCalls
[index
];
362 if (tempCall
->CallIndex() == kOutgoingPlaceholderCallIndex
) {
363 NS_ASSERTION(!outgoingCall
, "More than one outgoing call not supported!");
364 NS_ASSERTION(tempCall
->CallState() ==
365 nsIRadioInterfaceLayer::CALL_STATE_DIALING
,
366 "Something really wrong here!");
367 // Stash this for later, we may need it if aCallIndex doesn't match one of
369 outgoingCall
= tempCall
;
370 } else if (tempCall
->CallIndex() == aCallIndex
) {
371 // We already know about this call so just update its state.
372 modifiedCall
= tempCall
;
373 outgoingCall
= nullptr;
378 // If nothing matched above and the call state isn't incoming but we do have
379 // an outgoing call then we must be seeing a status update for our outgoing
382 aCallState
!= nsIRadioInterfaceLayer::CALL_STATE_INCOMING
&&
384 outgoingCall
->UpdateCallIndex(aCallIndex
);
385 modifiedCall
.swap(outgoingCall
);
389 // See if this should replace our current active call.
391 mActiveCall
= modifiedCall
;
392 } else if (mActiveCall
&& mActiveCall
->CallIndex() == aCallIndex
) {
393 mActiveCall
= nullptr;
397 modifiedCall
->ChangeState(aCallState
);
402 // Didn't know anything about this call before now.
404 if (aCallState
== nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED
) {
405 // Do nothing since we didn't know anything about it before now and it's
406 // been ended already.
410 nsRefPtr
<TelephonyCall
> call
=
411 TelephonyCall::Create(this, aNumber
, aCallState
, aCallIndex
);
412 NS_ASSERTION(call
, "This should never fail!");
414 NS_ASSERTION(mCalls
.Contains(call
), "Should have auto-added new call!");
416 if (aCallState
== nsIRadioInterfaceLayer::CALL_STATE_INCOMING
) {
417 // Dispatch incoming event.
418 nsRefPtr
<CallEvent
> event
= CallEvent::Create(call
);
419 NS_ASSERTION(event
, "This should never fail!");
422 event
->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("incoming"));
423 NS_ENSURE_SUCCESS(rv
, rv
);
430 Telephony::EnumerateCallState(uint32_t aCallIndex
, uint16_t aCallState
,
431 const nsAString
& aNumber
, bool aIsActive
,
434 // Make sure we don't somehow add duplicates.
435 for (uint32_t index
= 0; index
< mCalls
.Length(); index
++) {
436 nsRefPtr
<TelephonyCall
>& tempCall
= mCalls
[index
];
437 if (tempCall
->CallIndex() == aCallIndex
) {
438 // We have the call already. Skip it.
444 nsRefPtr
<TelephonyCall
> call
=
445 TelephonyCall::Create(this, aNumber
, aCallState
, aCallIndex
);
446 NS_ASSERTION(call
, "This should never fail!");
448 NS_ASSERTION(mCalls
.Contains(call
), "Should have auto-added new call!");
455 Telephony::NotifyError(int32_t aCallIndex
,
456 const nsAString
& aError
)
458 nsRefPtr
<TelephonyCall
> callToNotify
;
459 if (!mCalls
.IsEmpty()) {
460 // The connection is not established yet. Get the latest call object.
461 if (aCallIndex
== -1) {
462 callToNotify
= mCalls
[mCalls
.Length() - 1];
464 // The connection has been established. Get the failed call.
465 for (uint32_t index
= 0; index
< mCalls
.Length(); index
++) {
466 nsRefPtr
<TelephonyCall
>& call
= mCalls
[index
];
467 if (call
->CallIndex() == aCallIndex
) {
476 NS_ERROR("Don't call me with a bad call index!");
477 return NS_ERROR_UNEXPECTED
;
480 if (mActiveCall
&& mActiveCall
->CallIndex() == callToNotify
->CallIndex()) {
481 mActiveCall
= nullptr;
484 // Set the call state to 'disconnected' and remove it from the calls list.
485 callToNotify
->NotifyError(aError
);
491 NS_NewTelephony(nsPIDOMWindow
* aWindow
, nsIDOMTelephony
** aTelephony
)
493 NS_ASSERTION(aWindow
, "Null pointer!");
495 nsPIDOMWindow
* innerWindow
= aWindow
->IsInnerWindow() ?
497 aWindow
->GetCurrentInnerWindow();
499 nsCOMPtr
<nsIPermissionManager
> permMgr
=
500 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID
);
501 NS_ENSURE_TRUE(permMgr
, NS_ERROR_UNEXPECTED
);
505 permMgr
->TestPermissionFromWindow(aWindow
, "telephony", &permission
);
506 NS_ENSURE_SUCCESS(rv
, rv
);
508 if (permission
!= nsIPermissionManager::ALLOW_ACTION
) {
509 *aTelephony
= nullptr;
513 nsCOMPtr
<nsIRILContentHelper
> ril
=
514 do_GetService(NS_RILCONTENTHELPER_CONTRACTID
);
515 NS_ENSURE_TRUE(ril
, NS_ERROR_UNEXPECTED
);
517 nsRefPtr
<Telephony
> telephony
= Telephony::Create(innerWindow
, ril
);
518 NS_ENSURE_TRUE(telephony
, NS_ERROR_UNEXPECTED
);
520 telephony
.forget(aTelephony
);