michael@0: /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=40: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TelephonyCall.h" michael@0: #include "mozilla/dom/TelephonyCallBinding.h" michael@0: michael@0: #include "mozilla/dom/DOMError.h" michael@0: michael@0: #include "CallEvent.h" michael@0: #include "Telephony.h" michael@0: #include "TelephonyCallGroup.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: using mozilla::ErrorResult; michael@0: using mozilla::dom::telephony::kOutgoingPlaceholderCallIndex; michael@0: michael@0: // static michael@0: already_AddRefed michael@0: TelephonyCall::Create(Telephony* aTelephony, uint32_t aServiceId, michael@0: const nsAString& aNumber, uint16_t aCallState, michael@0: uint32_t aCallIndex, bool aEmergency, bool aIsConference, michael@0: bool aSwitchable, bool aMergeable) michael@0: { michael@0: NS_ASSERTION(aTelephony, "Null pointer!"); michael@0: NS_ASSERTION(!aNumber.IsEmpty(), "Empty number!"); michael@0: NS_ASSERTION(aCallIndex >= 1, "Invalid call index!"); michael@0: michael@0: nsRefPtr call = new TelephonyCall(aTelephony->GetOwner()); michael@0: michael@0: call->mTelephony = aTelephony; michael@0: call->mServiceId = aServiceId; michael@0: call->mNumber = aNumber; michael@0: call->mCallIndex = aCallIndex; michael@0: call->mError = nullptr; michael@0: call->mEmergency = aEmergency; michael@0: call->mGroup = aIsConference ? aTelephony->ConferenceGroup() : nullptr; michael@0: call->mSwitchable = aSwitchable; michael@0: call->mMergeable = aMergeable; michael@0: michael@0: call->ChangeStateInternal(aCallState, false); michael@0: michael@0: return call.forget(); michael@0: } michael@0: michael@0: TelephonyCall::TelephonyCall(nsPIDOMWindow* aOwner) michael@0: : DOMEventTargetHelper(aOwner), michael@0: mCallIndex(kOutgoingPlaceholderCallIndex), michael@0: mCallState(nsITelephonyProvider::CALL_STATE_UNKNOWN), michael@0: mLive(false) michael@0: { michael@0: } michael@0: michael@0: TelephonyCall::~TelephonyCall() michael@0: { michael@0: } michael@0: michael@0: JSObject* michael@0: TelephonyCall::WrapObject(JSContext* aCx) michael@0: { michael@0: return TelephonyCallBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: void michael@0: TelephonyCall::ChangeStateInternal(uint16_t aCallState, bool aFireEvents) michael@0: { michael@0: nsRefPtr kungFuDeathGrip(this); michael@0: michael@0: nsString stateString; michael@0: switch (aCallState) { michael@0: case nsITelephonyProvider::CALL_STATE_DIALING: michael@0: stateString.AssignLiteral("dialing"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_ALERTING: michael@0: stateString.AssignLiteral("alerting"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_CONNECTING: michael@0: stateString.AssignLiteral("connecting"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_CONNECTED: michael@0: stateString.AssignLiteral("connected"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_HOLDING: michael@0: stateString.AssignLiteral("holding"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_HELD: michael@0: stateString.AssignLiteral("held"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_RESUMING: michael@0: stateString.AssignLiteral("resuming"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_DISCONNECTING: michael@0: stateString.AssignLiteral("disconnecting"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_DISCONNECTED: michael@0: stateString.AssignLiteral("disconnected"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_INCOMING: michael@0: stateString.AssignLiteral("incoming"); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Unknown state!"); michael@0: } michael@0: michael@0: mState = stateString; michael@0: mCallState = aCallState; michael@0: michael@0: if (aCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) { michael@0: NS_ASSERTION(mLive, "Should be live!"); michael@0: mLive = false; michael@0: if (mGroup) { michael@0: mGroup->RemoveCall(this); michael@0: } else { michael@0: mTelephony->RemoveCall(this); michael@0: } michael@0: } else if (!mLive) { michael@0: mLive = true; michael@0: if (mGroup) { michael@0: mGroup->AddCall(this); michael@0: } else { michael@0: mTelephony->AddCall(this); michael@0: } michael@0: } michael@0: michael@0: if (aFireEvents) { michael@0: nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("statechange"), this); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to dispatch specific event!"); michael@0: } michael@0: michael@0: // This can change if the statechange handler called back here... Need to michael@0: // figure out something smarter. michael@0: if (mCallState == aCallState) { michael@0: rv = DispatchCallEvent(stateString, this); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to dispatch specific event!"); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: TelephonyCall::DispatchCallEvent(const nsAString& aType, michael@0: TelephonyCall* aCall) michael@0: { michael@0: MOZ_ASSERT(aCall); michael@0: michael@0: nsRefPtr event = CallEvent::Create(this, aType, aCall, false, false); michael@0: michael@0: return DispatchTrustedEvent(event); michael@0: } michael@0: michael@0: void michael@0: TelephonyCall::NotifyError(const nsAString& aError) michael@0: { michael@0: // Set the error string michael@0: NS_ASSERTION(!mError, "Already have an error?"); michael@0: michael@0: mError = new DOMError(GetOwner(), aError); michael@0: michael@0: // Do the state transitions michael@0: ChangeStateInternal(nsITelephonyProvider::CALL_STATE_DISCONNECTED, true); michael@0: michael@0: nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("error"), this); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to dispatch error event!"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: TelephonyCall::ChangeGroup(TelephonyCallGroup* aGroup) michael@0: { michael@0: mGroup = aGroup; michael@0: michael@0: nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("groupchange"), this); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to dispatch error event!"); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(TelephonyCall, michael@0: DOMEventTargetHelper, michael@0: mTelephony, michael@0: mError, michael@0: mGroup); michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TelephonyCall) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(TelephonyCall, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(TelephonyCall, DOMEventTargetHelper) michael@0: michael@0: // TelephonyCall WebIDL michael@0: michael@0: already_AddRefed michael@0: TelephonyCall::GetError() const michael@0: { michael@0: nsRefPtr error = mError; michael@0: return error.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: TelephonyCall::GetGroup() const michael@0: { michael@0: nsRefPtr group = mGroup; michael@0: return group.forget(); michael@0: } michael@0: michael@0: void michael@0: TelephonyCall::Answer(ErrorResult& aRv) michael@0: { michael@0: if (mCallState != nsITelephonyProvider::CALL_STATE_INCOMING) { michael@0: NS_WARNING("Answer on non-incoming call ignored!"); michael@0: return; michael@0: } michael@0: michael@0: nsresult rv = mTelephony->Provider()->AnswerCall(mServiceId, mCallIndex); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: michael@0: ChangeStateInternal(nsITelephonyProvider::CALL_STATE_CONNECTING, true); michael@0: } michael@0: michael@0: void michael@0: TelephonyCall::HangUp(ErrorResult& aRv) michael@0: { michael@0: if (mCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTING || michael@0: mCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) { michael@0: NS_WARNING("HangUp on previously disconnected call ignored!"); michael@0: return; michael@0: } michael@0: michael@0: nsresult rv = mCallState == nsITelephonyProvider::CALL_STATE_INCOMING ? michael@0: mTelephony->Provider()->RejectCall(mServiceId, mCallIndex) : michael@0: mTelephony->Provider()->HangUp(mServiceId, mCallIndex); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: michael@0: ChangeStateInternal(nsITelephonyProvider::CALL_STATE_DISCONNECTING, true); michael@0: } michael@0: michael@0: void michael@0: TelephonyCall::Hold(ErrorResult& aRv) michael@0: { michael@0: if (mCallState != nsITelephonyProvider::CALL_STATE_CONNECTED) { michael@0: NS_WARNING("Hold non-connected call ignored!"); michael@0: return; michael@0: } michael@0: michael@0: if (mGroup) { michael@0: NS_WARNING("Hold a call in conference ignored!"); michael@0: return; michael@0: } michael@0: michael@0: if (!mSwitchable) { michael@0: NS_WARNING("Hold a non-switchable call ignored!"); michael@0: return; michael@0: } michael@0: michael@0: nsresult rv = mTelephony->Provider()->HoldCall(mServiceId, mCallIndex); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: michael@0: if (!mSecondNumber.IsEmpty()) { michael@0: // No state transition when we switch two numbers within one TelephonyCall michael@0: // object. Otherwise, the state here will be inconsistent with the backend michael@0: // RIL and will never be right. michael@0: return; michael@0: } michael@0: michael@0: ChangeStateInternal(nsITelephonyProvider::CALL_STATE_HOLDING, true); michael@0: } michael@0: michael@0: void michael@0: TelephonyCall::Resume(ErrorResult& aRv) michael@0: { michael@0: if (mCallState != nsITelephonyProvider::CALL_STATE_HELD) { michael@0: NS_WARNING("Resume non-held call ignored!"); michael@0: return; michael@0: } michael@0: michael@0: if (mGroup) { michael@0: NS_WARNING("Resume a call in conference ignored!"); michael@0: return; michael@0: } michael@0: michael@0: if (!mSwitchable) { michael@0: NS_WARNING("Resume a non-switchable call ignored!"); michael@0: return; michael@0: } michael@0: michael@0: nsresult rv = mTelephony->Provider()->ResumeCall(mServiceId, mCallIndex); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: michael@0: ChangeStateInternal(nsITelephonyProvider::CALL_STATE_RESUMING, true); michael@0: }