1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/telephony/Telephony.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,744 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "Telephony.h" 1.11 +#include "mozilla/dom/TelephonyBinding.h" 1.12 +#include "mozilla/dom/Promise.h" 1.13 + 1.14 +#include "nsIURI.h" 1.15 +#include "nsPIDOMWindow.h" 1.16 +#include "nsIPermissionManager.h" 1.17 + 1.18 +#include "mozilla/dom/UnionTypes.h" 1.19 +#include "mozilla/Preferences.h" 1.20 +#include "nsCharSeparatedTokenizer.h" 1.21 +#include "nsContentUtils.h" 1.22 +#include "nsCxPusher.h" 1.23 +#include "nsNetUtil.h" 1.24 +#include "nsServiceManagerUtils.h" 1.25 +#include "nsThreadUtils.h" 1.26 + 1.27 +#include "CallEvent.h" 1.28 +#include "CallsList.h" 1.29 +#include "TelephonyCall.h" 1.30 +#include "TelephonyCallGroup.h" 1.31 + 1.32 +using namespace mozilla::dom; 1.33 +using mozilla::ErrorResult; 1.34 +using mozilla::dom::telephony::kOutgoingPlaceholderCallIndex; 1.35 + 1.36 +class Telephony::Listener : public nsITelephonyListener 1.37 +{ 1.38 + Telephony* mTelephony; 1.39 + 1.40 +public: 1.41 + NS_DECL_ISUPPORTS 1.42 + NS_FORWARD_SAFE_NSITELEPHONYLISTENER(mTelephony) 1.43 + 1.44 + Listener(Telephony* aTelephony) 1.45 + : mTelephony(aTelephony) 1.46 + { 1.47 + MOZ_ASSERT(mTelephony); 1.48 + } 1.49 + 1.50 + virtual ~Listener() {} 1.51 + 1.52 + void 1.53 + Disconnect() 1.54 + { 1.55 + MOZ_ASSERT(mTelephony); 1.56 + mTelephony = nullptr; 1.57 + } 1.58 +}; 1.59 + 1.60 +class Telephony::Callback : public nsITelephonyCallback 1.61 +{ 1.62 + nsRefPtr<Telephony> mTelephony; 1.63 + nsRefPtr<Promise> mPromise; 1.64 + uint32_t mServiceId; 1.65 + nsString mNumber; 1.66 + 1.67 +public: 1.68 + NS_DECL_ISUPPORTS 1.69 + 1.70 + Callback(Telephony* aTelephony, Promise* aPromise, uint32_t aServiceId, 1.71 + const nsAString& aNumber) 1.72 + : mTelephony(aTelephony), mPromise(aPromise), mServiceId(aServiceId), 1.73 + mNumber(aNumber) 1.74 + { 1.75 + MOZ_ASSERT(mTelephony); 1.76 + } 1.77 + 1.78 + virtual ~Callback() {} 1.79 + 1.80 + NS_IMETHODIMP 1.81 + NotifyDialError(const nsAString& aError) 1.82 + { 1.83 + mPromise->MaybeReject(aError); 1.84 + return NS_OK; 1.85 + } 1.86 + 1.87 + NS_IMETHODIMP 1.88 + NotifyDialSuccess() 1.89 + { 1.90 + nsRefPtr<TelephonyCall> call = 1.91 + mTelephony->CreateNewDialingCall(mServiceId, mNumber); 1.92 + 1.93 + mPromise->MaybeResolve(call); 1.94 + return NS_OK; 1.95 + } 1.96 +}; 1.97 + 1.98 +class Telephony::EnumerationAck : public nsRunnable 1.99 +{ 1.100 + nsRefPtr<Telephony> mTelephony; 1.101 + 1.102 +public: 1.103 + EnumerationAck(Telephony* aTelephony) 1.104 + : mTelephony(aTelephony) 1.105 + { 1.106 + MOZ_ASSERT(mTelephony); 1.107 + } 1.108 + 1.109 + NS_IMETHOD Run() 1.110 + { 1.111 + mTelephony->NotifyCallsChanged(nullptr); 1.112 + return NS_OK; 1.113 + } 1.114 +}; 1.115 + 1.116 +Telephony::Telephony(nsPIDOMWindow* aOwner) 1.117 + : DOMEventTargetHelper(aOwner), mActiveCall(nullptr), mEnumerated(false) 1.118 +{ 1.119 +} 1.120 + 1.121 +Telephony::~Telephony() 1.122 +{ 1.123 + Shutdown(); 1.124 +} 1.125 + 1.126 +void 1.127 +Telephony::Shutdown() 1.128 +{ 1.129 + if (mListener) { 1.130 + mListener->Disconnect(); 1.131 + 1.132 + if (mProvider) { 1.133 + mProvider->UnregisterListener(mListener); 1.134 + mProvider = nullptr; 1.135 + } 1.136 + 1.137 + mListener = nullptr; 1.138 + } 1.139 +} 1.140 + 1.141 +JSObject* 1.142 +Telephony::WrapObject(JSContext* aCx) 1.143 +{ 1.144 + return TelephonyBinding::Wrap(aCx, this); 1.145 +} 1.146 + 1.147 +// static 1.148 +already_AddRefed<Telephony> 1.149 +Telephony::Create(nsPIDOMWindow* aOwner, ErrorResult& aRv) 1.150 +{ 1.151 + NS_ASSERTION(aOwner, "Null owner!"); 1.152 + 1.153 + nsCOMPtr<nsITelephonyProvider> ril = 1.154 + do_GetService(TELEPHONY_PROVIDER_CONTRACTID); 1.155 + if (!ril) { 1.156 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.157 + return nullptr; 1.158 + } 1.159 + 1.160 + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner); 1.161 + if (!sgo) { 1.162 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.163 + return nullptr; 1.164 + } 1.165 + 1.166 + nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext(); 1.167 + if (!scriptContext) { 1.168 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.169 + return nullptr; 1.170 + } 1.171 + 1.172 + nsRefPtr<Telephony> telephony = new Telephony(aOwner); 1.173 + 1.174 + telephony->mProvider = ril; 1.175 + telephony->mListener = new Listener(telephony); 1.176 + telephony->mCallsList = new CallsList(telephony); 1.177 + telephony->mGroup = TelephonyCallGroup::Create(telephony); 1.178 + 1.179 + nsresult rv = ril->EnumerateCalls(telephony->mListener); 1.180 + if (NS_FAILED(rv)) { 1.181 + aRv.Throw(rv); 1.182 + return nullptr; 1.183 + } 1.184 + 1.185 + return telephony.forget(); 1.186 +} 1.187 + 1.188 +// static 1.189 +bool 1.190 +Telephony::IsValidNumber(const nsAString& aNumber) 1.191 +{ 1.192 + return !aNumber.IsEmpty(); 1.193 +} 1.194 + 1.195 +// static 1.196 +uint32_t 1.197 +Telephony::GetNumServices() { 1.198 + return mozilla::Preferences::GetInt("ril.numRadioInterfaces", 1); 1.199 +} 1.200 + 1.201 +// static 1.202 +bool 1.203 +Telephony::IsValidServiceId(uint32_t aServiceId) 1.204 +{ 1.205 + return aServiceId < GetNumServices(); 1.206 +} 1.207 + 1.208 +// static 1.209 +bool 1.210 +Telephony::IsActiveState(uint16_t aCallState) { 1.211 + return aCallState == nsITelephonyProvider::CALL_STATE_DIALING || 1.212 + aCallState == nsITelephonyProvider::CALL_STATE_ALERTING || 1.213 + aCallState == nsITelephonyProvider::CALL_STATE_CONNECTED; 1.214 +} 1.215 + 1.216 +uint32_t 1.217 +Telephony::ProvidedOrDefaultServiceId(const Optional<uint32_t>& aServiceId) 1.218 +{ 1.219 + if (aServiceId.WasPassed()) { 1.220 + return aServiceId.Value(); 1.221 + } else { 1.222 + uint32_t serviceId = 0; 1.223 + mProvider->GetDefaultServiceId(&serviceId); 1.224 + return serviceId; 1.225 + } 1.226 +} 1.227 + 1.228 +bool 1.229 +Telephony::HasDialingCall() 1.230 +{ 1.231 + for (uint32_t i = 0; i < mCalls.Length(); i++) { 1.232 + const nsRefPtr<TelephonyCall>& call = mCalls[i]; 1.233 + if (call->CallState() > nsITelephonyProvider::CALL_STATE_UNKNOWN && 1.234 + call->CallState() < nsITelephonyProvider::CALL_STATE_CONNECTED) { 1.235 + return true; 1.236 + } 1.237 + } 1.238 + 1.239 + return false; 1.240 +} 1.241 + 1.242 +bool 1.243 +Telephony::MatchActiveCall(TelephonyCall* aCall) 1.244 +{ 1.245 + return (mActiveCall && 1.246 + mActiveCall->CallIndex() == aCall->CallIndex() && 1.247 + mActiveCall->ServiceId() == aCall->ServiceId()); 1.248 +} 1.249 + 1.250 +already_AddRefed<Promise> 1.251 +Telephony::DialInternal(uint32_t aServiceId, const nsAString& aNumber, 1.252 + bool aIsEmergency) 1.253 +{ 1.254 + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner()); 1.255 + if (!global) { 1.256 + return nullptr; 1.257 + } 1.258 + 1.259 + nsRefPtr<Promise> promise = new Promise(global); 1.260 + 1.261 + if (!IsValidNumber(aNumber) || !IsValidServiceId(aServiceId)) { 1.262 + promise->MaybeReject(NS_LITERAL_STRING("InvalidAccessError")); 1.263 + return promise.forget(); 1.264 + } 1.265 + 1.266 + // We only support one outgoing call at a time. 1.267 + if (HasDialingCall()) { 1.268 + promise->MaybeReject(NS_LITERAL_STRING("InvalidStateError")); 1.269 + return promise.forget(); 1.270 + } 1.271 + 1.272 + nsCOMPtr<nsITelephonyCallback> callback = 1.273 + new Callback(this, promise, aServiceId, aNumber); 1.274 + nsresult rv = mProvider->Dial(aServiceId, aNumber, aIsEmergency, callback); 1.275 + if (NS_FAILED(rv)) { 1.276 + promise->MaybeReject(NS_LITERAL_STRING("InvalidStateError")); 1.277 + return promise.forget(); 1.278 + } 1.279 + 1.280 + return promise.forget(); 1.281 +} 1.282 + 1.283 +already_AddRefed<TelephonyCall> 1.284 +Telephony::CreateNewDialingCall(uint32_t aServiceId, const nsAString& aNumber) 1.285 +{ 1.286 + nsRefPtr<TelephonyCall> call = 1.287 + TelephonyCall::Create(this, aServiceId, aNumber, 1.288 + nsITelephonyProvider::CALL_STATE_DIALING); 1.289 + NS_ASSERTION(call, "This should never fail!"); 1.290 + 1.291 + NS_ASSERTION(mCalls.Contains(call), "Should have auto-added new call!"); 1.292 + 1.293 + return call.forget(); 1.294 +} 1.295 + 1.296 +nsresult 1.297 +Telephony::NotifyCallsChanged(TelephonyCall* aCall) 1.298 +{ 1.299 + return DispatchCallEvent(NS_LITERAL_STRING("callschanged"), aCall); 1.300 +} 1.301 + 1.302 +void 1.303 +Telephony::UpdateActiveCall(TelephonyCall* aCall, bool aIsActive) 1.304 +{ 1.305 + if (aIsActive) { 1.306 + mActiveCall = aCall; 1.307 + } else if (MatchActiveCall(aCall)) { 1.308 + mActiveCall = nullptr; 1.309 + } 1.310 +} 1.311 + 1.312 +already_AddRefed<TelephonyCall> 1.313 +Telephony::GetCall(uint32_t aServiceId, uint32_t aCallIndex) 1.314 +{ 1.315 + nsRefPtr<TelephonyCall> call; 1.316 + 1.317 + for (uint32_t i = 0; i < mCalls.Length(); i++) { 1.318 + nsRefPtr<TelephonyCall>& tempCall = mCalls[i]; 1.319 + if (tempCall->ServiceId() == aServiceId && 1.320 + tempCall->CallIndex() == aCallIndex) { 1.321 + call = tempCall; 1.322 + break; 1.323 + } 1.324 + } 1.325 + 1.326 + return call.forget(); 1.327 +} 1.328 + 1.329 +already_AddRefed<TelephonyCall> 1.330 +Telephony::GetOutgoingCall() 1.331 +{ 1.332 + nsRefPtr<TelephonyCall> call; 1.333 + 1.334 + for (uint32_t i = 0; i < mCalls.Length(); i++) { 1.335 + nsRefPtr<TelephonyCall>& tempCall = mCalls[i]; 1.336 + if (tempCall->CallIndex() == kOutgoingPlaceholderCallIndex) { 1.337 + NS_ASSERTION(!call, "More than one outgoing call not supported!"); 1.338 + NS_ASSERTION(tempCall->CallState() == nsITelephonyProvider::CALL_STATE_DIALING, 1.339 + "Something really wrong here!"); 1.340 + 1.341 + call = tempCall; 1.342 + // No break. We will search entire list to ensure only one outgoing call. 1.343 + } 1.344 + } 1.345 + 1.346 + return call.forget(); 1.347 +} 1.348 + 1.349 +already_AddRefed<TelephonyCall> 1.350 +Telephony::GetCallFromEverywhere(uint32_t aServiceId, uint32_t aCallIndex) 1.351 +{ 1.352 + nsRefPtr<TelephonyCall> call = GetCall(aServiceId, aCallIndex); 1.353 + 1.354 + if (!call) { 1.355 + call = mGroup->GetCall(aServiceId, aCallIndex); 1.356 + } 1.357 + 1.358 + return call.forget(); 1.359 +} 1.360 + 1.361 +NS_IMPL_CYCLE_COLLECTION_CLASS(Telephony) 1.362 + 1.363 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Telephony, 1.364 + DOMEventTargetHelper) 1.365 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCalls) 1.366 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallsList) 1.367 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGroup) 1.368 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.369 + 1.370 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Telephony, 1.371 + DOMEventTargetHelper) 1.372 + tmp->Shutdown(); 1.373 + tmp->mActiveCall = nullptr; 1.374 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCalls) 1.375 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallsList) 1.376 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGroup) 1.377 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.378 + 1.379 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Telephony) 1.380 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.381 + 1.382 +NS_IMPL_ADDREF_INHERITED(Telephony, DOMEventTargetHelper) 1.383 +NS_IMPL_RELEASE_INHERITED(Telephony, DOMEventTargetHelper) 1.384 + 1.385 +NS_IMPL_ISUPPORTS(Telephony::Listener, nsITelephonyListener) 1.386 +NS_IMPL_ISUPPORTS(Telephony::Callback, nsITelephonyCallback) 1.387 + 1.388 +// Telephony WebIDL 1.389 + 1.390 +already_AddRefed<Promise> 1.391 +Telephony::Dial(const nsAString& aNumber, const Optional<uint32_t>& aServiceId) 1.392 +{ 1.393 + uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); 1.394 + nsRefPtr<Promise> promise = DialInternal(serviceId, aNumber, false); 1.395 + return promise.forget(); 1.396 +} 1.397 + 1.398 +already_AddRefed<Promise> 1.399 +Telephony::DialEmergency(const nsAString& aNumber, 1.400 + const Optional<uint32_t>& aServiceId) 1.401 +{ 1.402 + uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); 1.403 + nsRefPtr<Promise> promise = DialInternal(serviceId, aNumber, true); 1.404 + return promise.forget(); 1.405 +} 1.406 + 1.407 +void 1.408 +Telephony::StartTone(const nsAString& aDTMFChar, 1.409 + const Optional<uint32_t>& aServiceId, 1.410 + ErrorResult& aRv) 1.411 +{ 1.412 + uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); 1.413 + 1.414 + if (aDTMFChar.IsEmpty()) { 1.415 + NS_WARNING("Empty tone string will be ignored"); 1.416 + return; 1.417 + } 1.418 + 1.419 + if (aDTMFChar.Length() > 1 || !IsValidServiceId(serviceId)) { 1.420 + aRv.Throw(NS_ERROR_INVALID_ARG); 1.421 + return; 1.422 + } 1.423 + 1.424 + aRv = mProvider->StartTone(serviceId, aDTMFChar); 1.425 +} 1.426 + 1.427 +void 1.428 +Telephony::StopTone(const Optional<uint32_t>& aServiceId, ErrorResult& aRv) 1.429 +{ 1.430 + uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); 1.431 + 1.432 + if (!IsValidServiceId(serviceId)) { 1.433 + aRv.Throw(NS_ERROR_INVALID_ARG); 1.434 + return; 1.435 + } 1.436 + 1.437 + aRv = mProvider->StopTone(serviceId); 1.438 +} 1.439 + 1.440 +bool 1.441 +Telephony::GetMuted(ErrorResult& aRv) const 1.442 +{ 1.443 + bool muted = false; 1.444 + aRv = mProvider->GetMicrophoneMuted(&muted); 1.445 + 1.446 + return muted; 1.447 +} 1.448 + 1.449 +void 1.450 +Telephony::SetMuted(bool aMuted, ErrorResult& aRv) 1.451 +{ 1.452 + aRv = mProvider->SetMicrophoneMuted(aMuted); 1.453 +} 1.454 + 1.455 +bool 1.456 +Telephony::GetSpeakerEnabled(ErrorResult& aRv) const 1.457 +{ 1.458 + bool enabled = false; 1.459 + aRv = mProvider->GetSpeakerEnabled(&enabled); 1.460 + 1.461 + return enabled; 1.462 +} 1.463 + 1.464 +void 1.465 +Telephony::SetSpeakerEnabled(bool aEnabled, ErrorResult& aRv) 1.466 +{ 1.467 + aRv = mProvider->SetSpeakerEnabled(aEnabled); 1.468 +} 1.469 + 1.470 +void 1.471 +Telephony::GetActive(Nullable<OwningTelephonyCallOrTelephonyCallGroup>& aValue) 1.472 +{ 1.473 + if (mActiveCall) { 1.474 + aValue.SetValue().SetAsTelephonyCall() = mActiveCall; 1.475 + } else if (mGroup->CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED) { 1.476 + aValue.SetValue().SetAsTelephonyCallGroup() = mGroup; 1.477 + } else { 1.478 + aValue.SetNull(); 1.479 + } 1.480 +} 1.481 + 1.482 +already_AddRefed<CallsList> 1.483 +Telephony::Calls() const 1.484 +{ 1.485 + nsRefPtr<CallsList> list = mCallsList; 1.486 + return list.forget(); 1.487 +} 1.488 + 1.489 +already_AddRefed<TelephonyCallGroup> 1.490 +Telephony::ConferenceGroup() const 1.491 +{ 1.492 + nsRefPtr<TelephonyCallGroup> group = mGroup; 1.493 + return group.forget(); 1.494 +} 1.495 + 1.496 +// EventTarget 1.497 + 1.498 +void 1.499 +Telephony::EventListenerAdded(nsIAtom* aType) 1.500 +{ 1.501 + if (aType == nsGkAtoms::oncallschanged) { 1.502 + // Fire oncallschanged on the next tick if the calls array is ready. 1.503 + EnqueueEnumerationAck(); 1.504 + } 1.505 +} 1.506 + 1.507 +// nsITelephonyListener 1.508 + 1.509 +NS_IMETHODIMP 1.510 +Telephony::CallStateChanged(uint32_t aServiceId, uint32_t aCallIndex, 1.511 + uint16_t aCallState, const nsAString& aNumber, 1.512 + bool aIsActive, bool aIsOutgoing, bool aIsEmergency, 1.513 + bool aIsConference, bool aIsSwitchable, bool aIsMergeable) 1.514 +{ 1.515 + nsRefPtr<TelephonyCall> modifiedCall 1.516 + = GetCallFromEverywhere(aServiceId, aCallIndex); 1.517 + 1.518 + // Try to use the outgoing call if we don't find the modified call. 1.519 + if (!modifiedCall) { 1.520 + nsRefPtr<TelephonyCall> outgoingCall = GetOutgoingCall(); 1.521 + 1.522 + // If the call state isn't incoming but we do have an outgoing call then 1.523 + // we must be seeing a status update for our outgoing call. 1.524 + if (outgoingCall && 1.525 + aCallState != nsITelephonyProvider::CALL_STATE_INCOMING) { 1.526 + outgoingCall->UpdateCallIndex(aCallIndex); 1.527 + outgoingCall->UpdateEmergency(aIsEmergency); 1.528 + modifiedCall.swap(outgoingCall); 1.529 + } 1.530 + } 1.531 + 1.532 + if (modifiedCall) { 1.533 + modifiedCall->UpdateSwitchable(aIsSwitchable); 1.534 + modifiedCall->UpdateMergeable(aIsMergeable); 1.535 + 1.536 + if (!aIsConference) { 1.537 + UpdateActiveCall(modifiedCall, aIsActive); 1.538 + } 1.539 + 1.540 + if (modifiedCall->CallState() != aCallState) { 1.541 + // We don't fire the statechange event on a call in conference here. 1.542 + // Instead, the event will be fired later in 1.543 + // TelephonyCallGroup::ChangeState(). Thus the sequence of firing the 1.544 + // statechange events is guaranteed: first on TelephonyCallGroup then on 1.545 + // individual TelephonyCall objects. 1.546 + bool fireEvent = !aIsConference; 1.547 + modifiedCall->ChangeStateInternal(aCallState, fireEvent); 1.548 + } 1.549 + 1.550 + nsRefPtr<TelephonyCallGroup> group = modifiedCall->GetGroup(); 1.551 + 1.552 + if (!group && aIsConference) { 1.553 + // Add to conference. 1.554 + NS_ASSERTION(mCalls.Contains(modifiedCall), "Should in mCalls"); 1.555 + mGroup->AddCall(modifiedCall); 1.556 + RemoveCall(modifiedCall); 1.557 + } else if (group && !aIsConference) { 1.558 + // Remove from conference. 1.559 + NS_ASSERTION(mGroup->CallsArray().Contains(modifiedCall), "Should in mGroup"); 1.560 + mGroup->RemoveCall(modifiedCall); 1.561 + AddCall(modifiedCall); 1.562 + } 1.563 + 1.564 + return NS_OK; 1.565 + } 1.566 + 1.567 + // Do nothing since we didn't know anything about it before now and it's 1.568 + // ended already. 1.569 + if (aCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) { 1.570 + return NS_OK; 1.571 + } 1.572 + 1.573 + // Didn't find this call in mCalls or mGroup. Create a new call. 1.574 + nsRefPtr<TelephonyCall> call = 1.575 + TelephonyCall::Create(this, aServiceId, aNumber, aCallState, aCallIndex, 1.576 + aIsEmergency, aIsConference, aIsSwitchable, 1.577 + aIsMergeable); 1.578 + NS_ASSERTION(call, "This should never fail!"); 1.579 + 1.580 + NS_ASSERTION(aIsConference ? mGroup->CallsArray().Contains(call) : 1.581 + mCalls.Contains(call), 1.582 + "Should have auto-added new call!"); 1.583 + 1.584 + if (aCallState == nsITelephonyProvider::CALL_STATE_INCOMING) { 1.585 + nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("incoming"), call); 1.586 + NS_ENSURE_SUCCESS(rv, rv); 1.587 + } 1.588 + 1.589 + return NS_OK; 1.590 +} 1.591 + 1.592 +NS_IMETHODIMP 1.593 +Telephony::ConferenceCallStateChanged(uint16_t aCallState) 1.594 +{ 1.595 + mGroup->ChangeState(aCallState); 1.596 + return NS_OK; 1.597 +} 1.598 + 1.599 +NS_IMETHODIMP 1.600 +Telephony::EnumerateCallStateComplete() 1.601 +{ 1.602 + MOZ_ASSERT(!mEnumerated); 1.603 + 1.604 + mEnumerated = true; 1.605 + 1.606 + if (NS_FAILED(NotifyCallsChanged(nullptr))) { 1.607 + NS_WARNING("Failed to notify calls changed!"); 1.608 + } 1.609 + 1.610 + if (NS_FAILED(mProvider->RegisterListener(mListener))) { 1.611 + NS_WARNING("Failed to register listener!"); 1.612 + } 1.613 + return NS_OK; 1.614 +} 1.615 + 1.616 +NS_IMETHODIMP 1.617 +Telephony::EnumerateCallState(uint32_t aServiceId, uint32_t aCallIndex, 1.618 + uint16_t aCallState, const nsAString& aNumber, 1.619 + bool aIsActive, bool aIsOutgoing, bool aIsEmergency, 1.620 + bool aIsConference, bool aIsSwitchable, bool aIsMergeable) 1.621 +{ 1.622 + nsRefPtr<TelephonyCall> call; 1.623 + 1.624 + // We request calls enumeration in constructor, and the asynchronous result 1.625 + // will be sent back through the callback function EnumerateCallState(). 1.626 + // However, it is likely to have call state changes, i.e. CallStateChanged() 1.627 + // being called, before the enumeration result comes back. We'd make sure 1.628 + // we don't somehow add duplicates due to the race condition. 1.629 + call = GetCallFromEverywhere(aServiceId, aCallIndex); 1.630 + if (call) { 1.631 + return NS_OK; 1.632 + } 1.633 + 1.634 + // Didn't know anything about this call before now. 1.635 + call = TelephonyCall::Create(this, aServiceId, aNumber, aCallState, 1.636 + aCallIndex, aIsEmergency, aIsConference, 1.637 + aIsSwitchable, aIsMergeable); 1.638 + NS_ASSERTION(call, "This should never fail!"); 1.639 + 1.640 + NS_ASSERTION(aIsConference ? mGroup->CallsArray().Contains(call) : 1.641 + mCalls.Contains(call), 1.642 + "Should have auto-added new call!"); 1.643 + 1.644 + return NS_OK; 1.645 +} 1.646 + 1.647 +NS_IMETHODIMP 1.648 +Telephony::SupplementaryServiceNotification(uint32_t aServiceId, 1.649 + int32_t aCallIndex, 1.650 + uint16_t aNotification) 1.651 +{ 1.652 + nsRefPtr<TelephonyCall> associatedCall; 1.653 + if (!mCalls.IsEmpty()) { 1.654 + associatedCall = GetCall(aServiceId, aCallIndex); 1.655 + } 1.656 + 1.657 + nsresult rv; 1.658 + switch (aNotification) { 1.659 + case nsITelephonyProvider::NOTIFICATION_REMOTE_HELD: 1.660 + rv = DispatchCallEvent(NS_LITERAL_STRING("remoteheld"), associatedCall); 1.661 + break; 1.662 + case nsITelephonyProvider::NOTIFICATION_REMOTE_RESUMED: 1.663 + rv = DispatchCallEvent(NS_LITERAL_STRING("remoteresumed"), associatedCall); 1.664 + break; 1.665 + default: 1.666 + NS_ERROR("Got a bad notification!"); 1.667 + return NS_ERROR_UNEXPECTED; 1.668 + } 1.669 + 1.670 + NS_ENSURE_SUCCESS(rv, rv); 1.671 + return NS_OK; 1.672 +} 1.673 + 1.674 +NS_IMETHODIMP 1.675 +Telephony::NotifyError(uint32_t aServiceId, 1.676 + int32_t aCallIndex, 1.677 + const nsAString& aError) 1.678 +{ 1.679 + if (mCalls.IsEmpty()) { 1.680 + NS_ERROR("No existing call!"); 1.681 + return NS_ERROR_UNEXPECTED; 1.682 + } 1.683 + 1.684 + nsRefPtr<TelephonyCall> callToNotify = GetCall(aServiceId, aCallIndex); 1.685 + if (!callToNotify) { 1.686 + NS_ERROR("Don't call me with a bad call index!"); 1.687 + return NS_ERROR_UNEXPECTED; 1.688 + } 1.689 + 1.690 + UpdateActiveCall(callToNotify, false); 1.691 + 1.692 + // Set the call state to 'disconnected' and remove it from the calls list. 1.693 + callToNotify->NotifyError(aError); 1.694 + 1.695 + return NS_OK; 1.696 +} 1.697 + 1.698 +NS_IMETHODIMP 1.699 +Telephony::NotifyCdmaCallWaiting(uint32_t aServiceId, const nsAString& aNumber) 1.700 +{ 1.701 + MOZ_ASSERT(mCalls.Length() == 1); 1.702 + 1.703 + nsRefPtr<TelephonyCall> callToNotify = mCalls[0]; 1.704 + MOZ_ASSERT(callToNotify && callToNotify->ServiceId() == aServiceId); 1.705 + 1.706 + callToNotify->UpdateSecondNumber(aNumber); 1.707 + DispatchCallEvent(NS_LITERAL_STRING("callschanged"), callToNotify); 1.708 + return NS_OK; 1.709 +} 1.710 + 1.711 +NS_IMETHODIMP 1.712 +Telephony::NotifyConferenceError(const nsAString& aName, 1.713 + const nsAString& aMessage) 1.714 +{ 1.715 + mGroup->NotifyError(aName, aMessage); 1.716 + return NS_OK; 1.717 +} 1.718 + 1.719 +nsresult 1.720 +Telephony::DispatchCallEvent(const nsAString& aType, 1.721 + TelephonyCall* aCall) 1.722 +{ 1.723 + // The call may be null in following cases: 1.724 + // 1. callschanged when notifying enumeration being completed 1.725 + // 2. remoteheld/remoteresumed. 1.726 + MOZ_ASSERT(aCall || 1.727 + aType.EqualsLiteral("callschanged") || 1.728 + aType.EqualsLiteral("remoteheld") || 1.729 + aType.EqualsLiteral("remtoeresumed")); 1.730 + 1.731 + nsRefPtr<CallEvent> event = CallEvent::Create(this, aType, aCall, false, false); 1.732 + 1.733 + return DispatchTrustedEvent(event); 1.734 +} 1.735 + 1.736 +void 1.737 +Telephony::EnqueueEnumerationAck() 1.738 +{ 1.739 + if (!mEnumerated) { 1.740 + return; 1.741 + } 1.742 + 1.743 + nsCOMPtr<nsIRunnable> task = new EnumerationAck(this); 1.744 + if (NS_FAILED(NS_DispatchToCurrentThread(task))) { 1.745 + NS_WARNING("Failed to dispatch to current thread!"); 1.746 + } 1.747 +}