michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 "Telephony.h" michael@0: #include "mozilla/dom/TelephonyBinding.h" michael@0: #include "mozilla/dom/Promise.h" michael@0: michael@0: #include "nsIURI.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIPermissionManager.h" michael@0: michael@0: #include "mozilla/dom/UnionTypes.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsCharSeparatedTokenizer.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: #include "CallEvent.h" michael@0: #include "CallsList.h" michael@0: #include "TelephonyCall.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: class Telephony::Listener : public nsITelephonyListener michael@0: { michael@0: Telephony* mTelephony; michael@0: michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_FORWARD_SAFE_NSITELEPHONYLISTENER(mTelephony) michael@0: michael@0: Listener(Telephony* aTelephony) michael@0: : mTelephony(aTelephony) michael@0: { michael@0: MOZ_ASSERT(mTelephony); michael@0: } michael@0: michael@0: virtual ~Listener() {} michael@0: michael@0: void michael@0: Disconnect() michael@0: { michael@0: MOZ_ASSERT(mTelephony); michael@0: mTelephony = nullptr; michael@0: } michael@0: }; michael@0: michael@0: class Telephony::Callback : public nsITelephonyCallback michael@0: { michael@0: nsRefPtr mTelephony; michael@0: nsRefPtr mPromise; michael@0: uint32_t mServiceId; michael@0: nsString mNumber; michael@0: michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: Callback(Telephony* aTelephony, Promise* aPromise, uint32_t aServiceId, michael@0: const nsAString& aNumber) michael@0: : mTelephony(aTelephony), mPromise(aPromise), mServiceId(aServiceId), michael@0: mNumber(aNumber) michael@0: { michael@0: MOZ_ASSERT(mTelephony); michael@0: } michael@0: michael@0: virtual ~Callback() {} michael@0: michael@0: NS_IMETHODIMP michael@0: NotifyDialError(const nsAString& aError) michael@0: { michael@0: mPromise->MaybeReject(aError); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: NotifyDialSuccess() michael@0: { michael@0: nsRefPtr call = michael@0: mTelephony->CreateNewDialingCall(mServiceId, mNumber); michael@0: michael@0: mPromise->MaybeResolve(call); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: class Telephony::EnumerationAck : public nsRunnable michael@0: { michael@0: nsRefPtr mTelephony; michael@0: michael@0: public: michael@0: EnumerationAck(Telephony* aTelephony) michael@0: : mTelephony(aTelephony) michael@0: { michael@0: MOZ_ASSERT(mTelephony); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: mTelephony->NotifyCallsChanged(nullptr); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: Telephony::Telephony(nsPIDOMWindow* aOwner) michael@0: : DOMEventTargetHelper(aOwner), mActiveCall(nullptr), mEnumerated(false) michael@0: { michael@0: } michael@0: michael@0: Telephony::~Telephony() michael@0: { michael@0: Shutdown(); michael@0: } michael@0: michael@0: void michael@0: Telephony::Shutdown() michael@0: { michael@0: if (mListener) { michael@0: mListener->Disconnect(); michael@0: michael@0: if (mProvider) { michael@0: mProvider->UnregisterListener(mListener); michael@0: mProvider = nullptr; michael@0: } michael@0: michael@0: mListener = nullptr; michael@0: } michael@0: } michael@0: michael@0: JSObject* michael@0: Telephony::WrapObject(JSContext* aCx) michael@0: { michael@0: return TelephonyBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: Telephony::Create(nsPIDOMWindow* aOwner, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(aOwner, "Null owner!"); michael@0: michael@0: nsCOMPtr ril = michael@0: do_GetService(TELEPHONY_PROVIDER_CONTRACTID); michael@0: if (!ril) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr sgo = do_QueryInterface(aOwner); michael@0: if (!sgo) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr scriptContext = sgo->GetContext(); michael@0: if (!scriptContext) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr telephony = new Telephony(aOwner); michael@0: michael@0: telephony->mProvider = ril; michael@0: telephony->mListener = new Listener(telephony); michael@0: telephony->mCallsList = new CallsList(telephony); michael@0: telephony->mGroup = TelephonyCallGroup::Create(telephony); michael@0: michael@0: nsresult rv = ril->EnumerateCalls(telephony->mListener); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return nullptr; michael@0: } michael@0: michael@0: return telephony.forget(); michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: Telephony::IsValidNumber(const nsAString& aNumber) michael@0: { michael@0: return !aNumber.IsEmpty(); michael@0: } michael@0: michael@0: // static michael@0: uint32_t michael@0: Telephony::GetNumServices() { michael@0: return mozilla::Preferences::GetInt("ril.numRadioInterfaces", 1); michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: Telephony::IsValidServiceId(uint32_t aServiceId) michael@0: { michael@0: return aServiceId < GetNumServices(); michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: Telephony::IsActiveState(uint16_t aCallState) { michael@0: return aCallState == nsITelephonyProvider::CALL_STATE_DIALING || michael@0: aCallState == nsITelephonyProvider::CALL_STATE_ALERTING || michael@0: aCallState == nsITelephonyProvider::CALL_STATE_CONNECTED; michael@0: } michael@0: michael@0: uint32_t michael@0: Telephony::ProvidedOrDefaultServiceId(const Optional& aServiceId) michael@0: { michael@0: if (aServiceId.WasPassed()) { michael@0: return aServiceId.Value(); michael@0: } else { michael@0: uint32_t serviceId = 0; michael@0: mProvider->GetDefaultServiceId(&serviceId); michael@0: return serviceId; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: Telephony::HasDialingCall() michael@0: { michael@0: for (uint32_t i = 0; i < mCalls.Length(); i++) { michael@0: const nsRefPtr& call = mCalls[i]; michael@0: if (call->CallState() > nsITelephonyProvider::CALL_STATE_UNKNOWN && michael@0: call->CallState() < nsITelephonyProvider::CALL_STATE_CONNECTED) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: Telephony::MatchActiveCall(TelephonyCall* aCall) michael@0: { michael@0: return (mActiveCall && michael@0: mActiveCall->CallIndex() == aCall->CallIndex() && michael@0: mActiveCall->ServiceId() == aCall->ServiceId()); michael@0: } michael@0: michael@0: already_AddRefed michael@0: Telephony::DialInternal(uint32_t aServiceId, const nsAString& aNumber, michael@0: bool aIsEmergency) michael@0: { michael@0: nsCOMPtr global = do_QueryInterface(GetOwner()); michael@0: if (!global) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr promise = new Promise(global); michael@0: michael@0: if (!IsValidNumber(aNumber) || !IsValidServiceId(aServiceId)) { michael@0: promise->MaybeReject(NS_LITERAL_STRING("InvalidAccessError")); michael@0: return promise.forget(); michael@0: } michael@0: michael@0: // We only support one outgoing call at a time. michael@0: if (HasDialingCall()) { michael@0: promise->MaybeReject(NS_LITERAL_STRING("InvalidStateError")); michael@0: return promise.forget(); michael@0: } michael@0: michael@0: nsCOMPtr callback = michael@0: new Callback(this, promise, aServiceId, aNumber); michael@0: nsresult rv = mProvider->Dial(aServiceId, aNumber, aIsEmergency, callback); michael@0: if (NS_FAILED(rv)) { michael@0: promise->MaybeReject(NS_LITERAL_STRING("InvalidStateError")); michael@0: return promise.forget(); michael@0: } michael@0: michael@0: return promise.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: Telephony::CreateNewDialingCall(uint32_t aServiceId, const nsAString& aNumber) michael@0: { michael@0: nsRefPtr call = michael@0: TelephonyCall::Create(this, aServiceId, aNumber, michael@0: nsITelephonyProvider::CALL_STATE_DIALING); michael@0: NS_ASSERTION(call, "This should never fail!"); michael@0: michael@0: NS_ASSERTION(mCalls.Contains(call), "Should have auto-added new call!"); michael@0: michael@0: return call.forget(); michael@0: } michael@0: michael@0: nsresult michael@0: Telephony::NotifyCallsChanged(TelephonyCall* aCall) michael@0: { michael@0: return DispatchCallEvent(NS_LITERAL_STRING("callschanged"), aCall); michael@0: } michael@0: michael@0: void michael@0: Telephony::UpdateActiveCall(TelephonyCall* aCall, bool aIsActive) michael@0: { michael@0: if (aIsActive) { michael@0: mActiveCall = aCall; michael@0: } else if (MatchActiveCall(aCall)) { michael@0: mActiveCall = nullptr; michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: Telephony::GetCall(uint32_t aServiceId, uint32_t aCallIndex) michael@0: { michael@0: nsRefPtr call; michael@0: michael@0: for (uint32_t i = 0; i < mCalls.Length(); i++) { michael@0: nsRefPtr& tempCall = mCalls[i]; michael@0: if (tempCall->ServiceId() == aServiceId && michael@0: tempCall->CallIndex() == aCallIndex) { michael@0: call = tempCall; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return call.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: Telephony::GetOutgoingCall() michael@0: { michael@0: nsRefPtr call; michael@0: michael@0: for (uint32_t i = 0; i < mCalls.Length(); i++) { michael@0: nsRefPtr& tempCall = mCalls[i]; michael@0: if (tempCall->CallIndex() == kOutgoingPlaceholderCallIndex) { michael@0: NS_ASSERTION(!call, "More than one outgoing call not supported!"); michael@0: NS_ASSERTION(tempCall->CallState() == nsITelephonyProvider::CALL_STATE_DIALING, michael@0: "Something really wrong here!"); michael@0: michael@0: call = tempCall; michael@0: // No break. We will search entire list to ensure only one outgoing call. michael@0: } michael@0: } michael@0: michael@0: return call.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: Telephony::GetCallFromEverywhere(uint32_t aServiceId, uint32_t aCallIndex) michael@0: { michael@0: nsRefPtr call = GetCall(aServiceId, aCallIndex); michael@0: michael@0: if (!call) { michael@0: call = mGroup->GetCall(aServiceId, aCallIndex); michael@0: } michael@0: michael@0: return call.forget(); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(Telephony) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Telephony, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCalls) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallsList) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGroup) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Telephony, michael@0: DOMEventTargetHelper) michael@0: tmp->Shutdown(); michael@0: tmp->mActiveCall = nullptr; michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mCalls) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallsList) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mGroup) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Telephony) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(Telephony, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(Telephony, DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_ISUPPORTS(Telephony::Listener, nsITelephonyListener) michael@0: NS_IMPL_ISUPPORTS(Telephony::Callback, nsITelephonyCallback) michael@0: michael@0: // Telephony WebIDL michael@0: michael@0: already_AddRefed michael@0: Telephony::Dial(const nsAString& aNumber, const Optional& aServiceId) michael@0: { michael@0: uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); michael@0: nsRefPtr promise = DialInternal(serviceId, aNumber, false); michael@0: return promise.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: Telephony::DialEmergency(const nsAString& aNumber, michael@0: const Optional& aServiceId) michael@0: { michael@0: uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); michael@0: nsRefPtr promise = DialInternal(serviceId, aNumber, true); michael@0: return promise.forget(); michael@0: } michael@0: michael@0: void michael@0: Telephony::StartTone(const nsAString& aDTMFChar, michael@0: const Optional& aServiceId, michael@0: ErrorResult& aRv) michael@0: { michael@0: uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); michael@0: michael@0: if (aDTMFChar.IsEmpty()) { michael@0: NS_WARNING("Empty tone string will be ignored"); michael@0: return; michael@0: } michael@0: michael@0: if (aDTMFChar.Length() > 1 || !IsValidServiceId(serviceId)) { michael@0: aRv.Throw(NS_ERROR_INVALID_ARG); michael@0: return; michael@0: } michael@0: michael@0: aRv = mProvider->StartTone(serviceId, aDTMFChar); michael@0: } michael@0: michael@0: void michael@0: Telephony::StopTone(const Optional& aServiceId, ErrorResult& aRv) michael@0: { michael@0: uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); michael@0: michael@0: if (!IsValidServiceId(serviceId)) { michael@0: aRv.Throw(NS_ERROR_INVALID_ARG); michael@0: return; michael@0: } michael@0: michael@0: aRv = mProvider->StopTone(serviceId); michael@0: } michael@0: michael@0: bool michael@0: Telephony::GetMuted(ErrorResult& aRv) const michael@0: { michael@0: bool muted = false; michael@0: aRv = mProvider->GetMicrophoneMuted(&muted); michael@0: michael@0: return muted; michael@0: } michael@0: michael@0: void michael@0: Telephony::SetMuted(bool aMuted, ErrorResult& aRv) michael@0: { michael@0: aRv = mProvider->SetMicrophoneMuted(aMuted); michael@0: } michael@0: michael@0: bool michael@0: Telephony::GetSpeakerEnabled(ErrorResult& aRv) const michael@0: { michael@0: bool enabled = false; michael@0: aRv = mProvider->GetSpeakerEnabled(&enabled); michael@0: michael@0: return enabled; michael@0: } michael@0: michael@0: void michael@0: Telephony::SetSpeakerEnabled(bool aEnabled, ErrorResult& aRv) michael@0: { michael@0: aRv = mProvider->SetSpeakerEnabled(aEnabled); michael@0: } michael@0: michael@0: void michael@0: Telephony::GetActive(Nullable& aValue) michael@0: { michael@0: if (mActiveCall) { michael@0: aValue.SetValue().SetAsTelephonyCall() = mActiveCall; michael@0: } else if (mGroup->CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED) { michael@0: aValue.SetValue().SetAsTelephonyCallGroup() = mGroup; michael@0: } else { michael@0: aValue.SetNull(); michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: Telephony::Calls() const michael@0: { michael@0: nsRefPtr list = mCallsList; michael@0: return list.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: Telephony::ConferenceGroup() const michael@0: { michael@0: nsRefPtr group = mGroup; michael@0: return group.forget(); michael@0: } michael@0: michael@0: // EventTarget michael@0: michael@0: void michael@0: Telephony::EventListenerAdded(nsIAtom* aType) michael@0: { michael@0: if (aType == nsGkAtoms::oncallschanged) { michael@0: // Fire oncallschanged on the next tick if the calls array is ready. michael@0: EnqueueEnumerationAck(); michael@0: } michael@0: } michael@0: michael@0: // nsITelephonyListener michael@0: michael@0: NS_IMETHODIMP michael@0: Telephony::CallStateChanged(uint32_t aServiceId, uint32_t aCallIndex, michael@0: uint16_t aCallState, const nsAString& aNumber, michael@0: bool aIsActive, bool aIsOutgoing, bool aIsEmergency, michael@0: bool aIsConference, bool aIsSwitchable, bool aIsMergeable) michael@0: { michael@0: nsRefPtr modifiedCall michael@0: = GetCallFromEverywhere(aServiceId, aCallIndex); michael@0: michael@0: // Try to use the outgoing call if we don't find the modified call. michael@0: if (!modifiedCall) { michael@0: nsRefPtr outgoingCall = GetOutgoingCall(); michael@0: michael@0: // If the call state isn't incoming but we do have an outgoing call then michael@0: // we must be seeing a status update for our outgoing call. michael@0: if (outgoingCall && michael@0: aCallState != nsITelephonyProvider::CALL_STATE_INCOMING) { michael@0: outgoingCall->UpdateCallIndex(aCallIndex); michael@0: outgoingCall->UpdateEmergency(aIsEmergency); michael@0: modifiedCall.swap(outgoingCall); michael@0: } michael@0: } michael@0: michael@0: if (modifiedCall) { michael@0: modifiedCall->UpdateSwitchable(aIsSwitchable); michael@0: modifiedCall->UpdateMergeable(aIsMergeable); michael@0: michael@0: if (!aIsConference) { michael@0: UpdateActiveCall(modifiedCall, aIsActive); michael@0: } michael@0: michael@0: if (modifiedCall->CallState() != aCallState) { michael@0: // We don't fire the statechange event on a call in conference here. michael@0: // Instead, the event will be fired later in michael@0: // TelephonyCallGroup::ChangeState(). Thus the sequence of firing the michael@0: // statechange events is guaranteed: first on TelephonyCallGroup then on michael@0: // individual TelephonyCall objects. michael@0: bool fireEvent = !aIsConference; michael@0: modifiedCall->ChangeStateInternal(aCallState, fireEvent); michael@0: } michael@0: michael@0: nsRefPtr group = modifiedCall->GetGroup(); michael@0: michael@0: if (!group && aIsConference) { michael@0: // Add to conference. michael@0: NS_ASSERTION(mCalls.Contains(modifiedCall), "Should in mCalls"); michael@0: mGroup->AddCall(modifiedCall); michael@0: RemoveCall(modifiedCall); michael@0: } else if (group && !aIsConference) { michael@0: // Remove from conference. michael@0: NS_ASSERTION(mGroup->CallsArray().Contains(modifiedCall), "Should in mGroup"); michael@0: mGroup->RemoveCall(modifiedCall); michael@0: AddCall(modifiedCall); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Do nothing since we didn't know anything about it before now and it's michael@0: // ended already. michael@0: if (aCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Didn't find this call in mCalls or mGroup. Create a new call. michael@0: nsRefPtr call = michael@0: TelephonyCall::Create(this, aServiceId, aNumber, aCallState, aCallIndex, michael@0: aIsEmergency, aIsConference, aIsSwitchable, michael@0: aIsMergeable); michael@0: NS_ASSERTION(call, "This should never fail!"); michael@0: michael@0: NS_ASSERTION(aIsConference ? mGroup->CallsArray().Contains(call) : michael@0: mCalls.Contains(call), michael@0: "Should have auto-added new call!"); michael@0: michael@0: if (aCallState == nsITelephonyProvider::CALL_STATE_INCOMING) { michael@0: nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("incoming"), call); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Telephony::ConferenceCallStateChanged(uint16_t aCallState) michael@0: { michael@0: mGroup->ChangeState(aCallState); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Telephony::EnumerateCallStateComplete() michael@0: { michael@0: MOZ_ASSERT(!mEnumerated); michael@0: michael@0: mEnumerated = true; michael@0: michael@0: if (NS_FAILED(NotifyCallsChanged(nullptr))) { michael@0: NS_WARNING("Failed to notify calls changed!"); michael@0: } michael@0: michael@0: if (NS_FAILED(mProvider->RegisterListener(mListener))) { michael@0: NS_WARNING("Failed to register listener!"); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Telephony::EnumerateCallState(uint32_t aServiceId, uint32_t aCallIndex, michael@0: uint16_t aCallState, const nsAString& aNumber, michael@0: bool aIsActive, bool aIsOutgoing, bool aIsEmergency, michael@0: bool aIsConference, bool aIsSwitchable, bool aIsMergeable) michael@0: { michael@0: nsRefPtr call; michael@0: michael@0: // We request calls enumeration in constructor, and the asynchronous result michael@0: // will be sent back through the callback function EnumerateCallState(). michael@0: // However, it is likely to have call state changes, i.e. CallStateChanged() michael@0: // being called, before the enumeration result comes back. We'd make sure michael@0: // we don't somehow add duplicates due to the race condition. michael@0: call = GetCallFromEverywhere(aServiceId, aCallIndex); michael@0: if (call) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Didn't know anything about this call before now. michael@0: call = TelephonyCall::Create(this, aServiceId, aNumber, aCallState, michael@0: aCallIndex, aIsEmergency, aIsConference, michael@0: aIsSwitchable, aIsMergeable); michael@0: NS_ASSERTION(call, "This should never fail!"); michael@0: michael@0: NS_ASSERTION(aIsConference ? mGroup->CallsArray().Contains(call) : michael@0: mCalls.Contains(call), michael@0: "Should have auto-added new call!"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Telephony::SupplementaryServiceNotification(uint32_t aServiceId, michael@0: int32_t aCallIndex, michael@0: uint16_t aNotification) michael@0: { michael@0: nsRefPtr associatedCall; michael@0: if (!mCalls.IsEmpty()) { michael@0: associatedCall = GetCall(aServiceId, aCallIndex); michael@0: } michael@0: michael@0: nsresult rv; michael@0: switch (aNotification) { michael@0: case nsITelephonyProvider::NOTIFICATION_REMOTE_HELD: michael@0: rv = DispatchCallEvent(NS_LITERAL_STRING("remoteheld"), associatedCall); michael@0: break; michael@0: case nsITelephonyProvider::NOTIFICATION_REMOTE_RESUMED: michael@0: rv = DispatchCallEvent(NS_LITERAL_STRING("remoteresumed"), associatedCall); michael@0: break; michael@0: default: michael@0: NS_ERROR("Got a bad notification!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Telephony::NotifyError(uint32_t aServiceId, michael@0: int32_t aCallIndex, michael@0: const nsAString& aError) michael@0: { michael@0: if (mCalls.IsEmpty()) { michael@0: NS_ERROR("No existing call!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsRefPtr callToNotify = GetCall(aServiceId, aCallIndex); michael@0: if (!callToNotify) { michael@0: NS_ERROR("Don't call me with a bad call index!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: UpdateActiveCall(callToNotify, false); michael@0: michael@0: // Set the call state to 'disconnected' and remove it from the calls list. michael@0: callToNotify->NotifyError(aError); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Telephony::NotifyCdmaCallWaiting(uint32_t aServiceId, const nsAString& aNumber) michael@0: { michael@0: MOZ_ASSERT(mCalls.Length() == 1); michael@0: michael@0: nsRefPtr callToNotify = mCalls[0]; michael@0: MOZ_ASSERT(callToNotify && callToNotify->ServiceId() == aServiceId); michael@0: michael@0: callToNotify->UpdateSecondNumber(aNumber); michael@0: DispatchCallEvent(NS_LITERAL_STRING("callschanged"), callToNotify); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Telephony::NotifyConferenceError(const nsAString& aName, michael@0: const nsAString& aMessage) michael@0: { michael@0: mGroup->NotifyError(aName, aMessage); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: Telephony::DispatchCallEvent(const nsAString& aType, michael@0: TelephonyCall* aCall) michael@0: { michael@0: // The call may be null in following cases: michael@0: // 1. callschanged when notifying enumeration being completed michael@0: // 2. remoteheld/remoteresumed. michael@0: MOZ_ASSERT(aCall || michael@0: aType.EqualsLiteral("callschanged") || michael@0: aType.EqualsLiteral("remoteheld") || michael@0: aType.EqualsLiteral("remtoeresumed")); 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: Telephony::EnqueueEnumerationAck() michael@0: { michael@0: if (!mEnumerated) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr task = new EnumerationAck(this); michael@0: if (NS_FAILED(NS_DispatchToCurrentThread(task))) { michael@0: NS_WARNING("Failed to dispatch to current thread!"); michael@0: } michael@0: }