Bug 828713 - soundtouch_config.h checks the non-existing MOZ_SAMPLE_TYPE_S16LE symbol...
[gecko.git] / dom / telephony / Telephony.cpp
blob5e8f5c773bc43e406d28ab471a8c0d5315761b7c
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/. */
7 #include "Telephony.h"
9 #include "nsIURI.h"
10 #include "nsIURL.h"
11 #include "nsPIDOMWindow.h"
13 #include "jsapi.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;
31 namespace {
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);
55 if (mRooted) {
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;
67 else {
68 gTelephonyList->RemoveElement(this);
72 // static
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();
117 void
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);
124 nsresult
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!");
134 mActiveCall = aCall;
135 } else if (mActiveCall && mActiveCall->CallIndex() == aCall->CallIndex()) {
136 mActiveCall = nullptr;
139 nsresult rv =
140 event->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("callschanged"));
141 NS_ENSURE_SUCCESS(rv, rv);
143 return NS_OK;
146 nsresult
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
158 // at a time.
159 NS_WARNING("Only permitted to dial one call at a time!");
160 return NS_ERROR_NOT_AVAILABLE;
164 nsresult rv;
165 if (isEmergency) {
166 rv = mRIL->DialEmergency(aNumber);
167 } else {
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);
184 return NS_OK;
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)
203 tmp->mCalls.Clear();
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)
220 NS_IMETHODIMP
221 Telephony::Dial(const nsAString& aNumber, nsIDOMTelephonyCall** aResult)
223 DialInternal(false, aNumber, aResult);
225 return NS_OK;
228 NS_IMETHODIMP
229 Telephony::DialEmergency(const nsAString& aNumber, nsIDOMTelephonyCall** aResult)
231 DialInternal(true, aNumber, aResult);
233 return NS_OK;
236 NS_IMETHODIMP
237 Telephony::GetMuted(bool* aMuted)
239 nsresult rv = mRIL->GetMicrophoneMuted(aMuted);
240 NS_ENSURE_SUCCESS(rv, rv);
242 return NS_OK;
245 NS_IMETHODIMP
246 Telephony::SetMuted(bool aMuted)
248 nsresult rv = mRIL->SetMicrophoneMuted(aMuted);
249 NS_ENSURE_SUCCESS(rv, rv);
251 return NS_OK;
254 NS_IMETHODIMP
255 Telephony::GetSpeakerEnabled(bool* aSpeakerEnabled)
257 nsresult rv = mRIL->GetSpeakerEnabled(aSpeakerEnabled);
258 NS_ENSURE_SUCCESS(rv, rv);
260 return NS_OK;
263 NS_IMETHODIMP
264 Telephony::SetSpeakerEnabled(bool aSpeakerEnabled)
266 nsresult rv = mRIL->SetSpeakerEnabled(aSpeakerEnabled);
267 NS_ENSURE_SUCCESS(rv, rv);
269 return NS_OK;
272 NS_IMETHODIMP
273 Telephony::GetActive(jsval* aActive)
275 if (!mActiveCall) {
276 aActive->setNull();
277 return NS_OK;
280 nsresult rv;
281 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
282 NS_ENSURE_SUCCESS(rv, rv);
283 if (sc) {
284 rv =
285 nsContentUtils::WrapNative(sc->GetNativeContext(),
286 sc->GetNativeGlobal(),
287 mActiveCall->ToISupports(), aActive);
288 NS_ENSURE_SUCCESS(rv, rv);
290 return NS_OK;
293 NS_IMETHODIMP
294 Telephony::GetCalls(jsval* aCalls)
296 JSObject* calls = mCallsArray;
297 if (!calls) {
298 nsresult rv;
299 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
300 NS_ENSURE_SUCCESS(rv, rv);
301 if (sc) {
302 rv = nsTArrayToJSArray(sc->GetNativeContext(), mCalls, &calls);
303 NS_ENSURE_SUCCESS(rv, rv);
305 if (!mRooted) {
306 NS_HOLD_JS_OBJECTS(this, Telephony);
307 mRooted = true;
310 mCallsArray = calls;
311 } else {
312 NS_ENSURE_SUCCESS(rv, rv);
316 aCalls->setObject(*calls);
317 return NS_OK;
320 NS_IMETHODIMP
321 Telephony::StartTone(const nsAString& aDTMFChar)
323 if (aDTMFChar.IsEmpty()) {
324 NS_WARNING("Empty tone string will be ignored");
325 return NS_OK;
328 if (aDTMFChar.Length() > 1) {
329 return NS_ERROR_INVALID_ARG;
332 nsresult rv = mRIL->StartTone(aDTMFChar);
333 NS_ENSURE_SUCCESS(rv, rv);
335 return NS_OK;
338 NS_IMETHODIMP
339 Telephony::StopTone()
341 nsresult rv = mRIL->StopTone();
342 NS_ENSURE_SUCCESS(rv, rv);
344 return NS_OK;
347 NS_IMPL_EVENT_HANDLER(Telephony, incoming)
348 NS_IMPL_EVENT_HANDLER(Telephony, callschanged)
350 NS_IMETHODIMP
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
368 // our other calls.
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;
374 break;
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
380 // call.
381 if (!modifiedCall &&
382 aCallState != nsIRadioInterfaceLayer::CALL_STATE_INCOMING &&
383 outgoingCall) {
384 outgoingCall->UpdateCallIndex(aCallIndex);
385 modifiedCall.swap(outgoingCall);
388 if (modifiedCall) {
389 // See if this should replace our current active call.
390 if (aIsActive) {
391 mActiveCall = modifiedCall;
392 } else if (mActiveCall && mActiveCall->CallIndex() == aCallIndex) {
393 mActiveCall = nullptr;
396 // Change state.
397 modifiedCall->ChangeState(aCallState);
399 return NS_OK;
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.
407 return NS_OK;
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!");
421 nsresult rv =
422 event->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("incoming"));
423 NS_ENSURE_SUCCESS(rv, rv);
426 return NS_OK;
429 NS_IMETHODIMP
430 Telephony::EnumerateCallState(uint32_t aCallIndex, uint16_t aCallState,
431 const nsAString& aNumber, bool aIsActive,
432 bool* aContinue)
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.
439 *aContinue = true;
440 return NS_OK;
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!");
450 *aContinue = true;
451 return NS_OK;
454 NS_IMETHODIMP
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];
463 } else {
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) {
468 callToNotify = call;
469 break;
475 if (!callToNotify) {
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);
487 return NS_OK;
490 nsresult
491 NS_NewTelephony(nsPIDOMWindow* aWindow, nsIDOMTelephony** aTelephony)
493 NS_ASSERTION(aWindow, "Null pointer!");
495 nsPIDOMWindow* innerWindow = aWindow->IsInnerWindow() ?
496 aWindow :
497 aWindow->GetCurrentInnerWindow();
499 nsCOMPtr<nsIPermissionManager> permMgr =
500 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
501 NS_ENSURE_TRUE(permMgr, NS_ERROR_UNEXPECTED);
503 uint32_t permission;
504 nsresult rv =
505 permMgr->TestPermissionFromWindow(aWindow, "telephony", &permission);
506 NS_ENSURE_SUCCESS(rv, rv);
508 if (permission != nsIPermissionManager::ALLOW_ACTION) {
509 *aTelephony = nullptr;
510 return NS_OK;
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);
521 return NS_OK;