1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/telephony/TelephonyCall.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,307 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=40: */ 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 "TelephonyCall.h" 1.11 +#include "mozilla/dom/TelephonyCallBinding.h" 1.12 + 1.13 +#include "mozilla/dom/DOMError.h" 1.14 + 1.15 +#include "CallEvent.h" 1.16 +#include "Telephony.h" 1.17 +#include "TelephonyCallGroup.h" 1.18 + 1.19 +using namespace mozilla::dom; 1.20 +using mozilla::ErrorResult; 1.21 +using mozilla::dom::telephony::kOutgoingPlaceholderCallIndex; 1.22 + 1.23 +// static 1.24 +already_AddRefed<TelephonyCall> 1.25 +TelephonyCall::Create(Telephony* aTelephony, uint32_t aServiceId, 1.26 + const nsAString& aNumber, uint16_t aCallState, 1.27 + uint32_t aCallIndex, bool aEmergency, bool aIsConference, 1.28 + bool aSwitchable, bool aMergeable) 1.29 +{ 1.30 + NS_ASSERTION(aTelephony, "Null pointer!"); 1.31 + NS_ASSERTION(!aNumber.IsEmpty(), "Empty number!"); 1.32 + NS_ASSERTION(aCallIndex >= 1, "Invalid call index!"); 1.33 + 1.34 + nsRefPtr<TelephonyCall> call = new TelephonyCall(aTelephony->GetOwner()); 1.35 + 1.36 + call->mTelephony = aTelephony; 1.37 + call->mServiceId = aServiceId; 1.38 + call->mNumber = aNumber; 1.39 + call->mCallIndex = aCallIndex; 1.40 + call->mError = nullptr; 1.41 + call->mEmergency = aEmergency; 1.42 + call->mGroup = aIsConference ? aTelephony->ConferenceGroup() : nullptr; 1.43 + call->mSwitchable = aSwitchable; 1.44 + call->mMergeable = aMergeable; 1.45 + 1.46 + call->ChangeStateInternal(aCallState, false); 1.47 + 1.48 + return call.forget(); 1.49 +} 1.50 + 1.51 +TelephonyCall::TelephonyCall(nsPIDOMWindow* aOwner) 1.52 + : DOMEventTargetHelper(aOwner), 1.53 + mCallIndex(kOutgoingPlaceholderCallIndex), 1.54 + mCallState(nsITelephonyProvider::CALL_STATE_UNKNOWN), 1.55 + mLive(false) 1.56 +{ 1.57 +} 1.58 + 1.59 +TelephonyCall::~TelephonyCall() 1.60 +{ 1.61 +} 1.62 + 1.63 +JSObject* 1.64 +TelephonyCall::WrapObject(JSContext* aCx) 1.65 +{ 1.66 + return TelephonyCallBinding::Wrap(aCx, this); 1.67 +} 1.68 + 1.69 +void 1.70 +TelephonyCall::ChangeStateInternal(uint16_t aCallState, bool aFireEvents) 1.71 +{ 1.72 + nsRefPtr<TelephonyCall> kungFuDeathGrip(this); 1.73 + 1.74 + nsString stateString; 1.75 + switch (aCallState) { 1.76 + case nsITelephonyProvider::CALL_STATE_DIALING: 1.77 + stateString.AssignLiteral("dialing"); 1.78 + break; 1.79 + case nsITelephonyProvider::CALL_STATE_ALERTING: 1.80 + stateString.AssignLiteral("alerting"); 1.81 + break; 1.82 + case nsITelephonyProvider::CALL_STATE_CONNECTING: 1.83 + stateString.AssignLiteral("connecting"); 1.84 + break; 1.85 + case nsITelephonyProvider::CALL_STATE_CONNECTED: 1.86 + stateString.AssignLiteral("connected"); 1.87 + break; 1.88 + case nsITelephonyProvider::CALL_STATE_HOLDING: 1.89 + stateString.AssignLiteral("holding"); 1.90 + break; 1.91 + case nsITelephonyProvider::CALL_STATE_HELD: 1.92 + stateString.AssignLiteral("held"); 1.93 + break; 1.94 + case nsITelephonyProvider::CALL_STATE_RESUMING: 1.95 + stateString.AssignLiteral("resuming"); 1.96 + break; 1.97 + case nsITelephonyProvider::CALL_STATE_DISCONNECTING: 1.98 + stateString.AssignLiteral("disconnecting"); 1.99 + break; 1.100 + case nsITelephonyProvider::CALL_STATE_DISCONNECTED: 1.101 + stateString.AssignLiteral("disconnected"); 1.102 + break; 1.103 + case nsITelephonyProvider::CALL_STATE_INCOMING: 1.104 + stateString.AssignLiteral("incoming"); 1.105 + break; 1.106 + default: 1.107 + NS_NOTREACHED("Unknown state!"); 1.108 + } 1.109 + 1.110 + mState = stateString; 1.111 + mCallState = aCallState; 1.112 + 1.113 + if (aCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) { 1.114 + NS_ASSERTION(mLive, "Should be live!"); 1.115 + mLive = false; 1.116 + if (mGroup) { 1.117 + mGroup->RemoveCall(this); 1.118 + } else { 1.119 + mTelephony->RemoveCall(this); 1.120 + } 1.121 + } else if (!mLive) { 1.122 + mLive = true; 1.123 + if (mGroup) { 1.124 + mGroup->AddCall(this); 1.125 + } else { 1.126 + mTelephony->AddCall(this); 1.127 + } 1.128 + } 1.129 + 1.130 + if (aFireEvents) { 1.131 + nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("statechange"), this); 1.132 + if (NS_FAILED(rv)) { 1.133 + NS_WARNING("Failed to dispatch specific event!"); 1.134 + } 1.135 + 1.136 + // This can change if the statechange handler called back here... Need to 1.137 + // figure out something smarter. 1.138 + if (mCallState == aCallState) { 1.139 + rv = DispatchCallEvent(stateString, this); 1.140 + if (NS_FAILED(rv)) { 1.141 + NS_WARNING("Failed to dispatch specific event!"); 1.142 + } 1.143 + } 1.144 + } 1.145 +} 1.146 + 1.147 +nsresult 1.148 +TelephonyCall::DispatchCallEvent(const nsAString& aType, 1.149 + TelephonyCall* aCall) 1.150 +{ 1.151 + MOZ_ASSERT(aCall); 1.152 + 1.153 + nsRefPtr<CallEvent> event = CallEvent::Create(this, aType, aCall, false, false); 1.154 + 1.155 + return DispatchTrustedEvent(event); 1.156 +} 1.157 + 1.158 +void 1.159 +TelephonyCall::NotifyError(const nsAString& aError) 1.160 +{ 1.161 + // Set the error string 1.162 + NS_ASSERTION(!mError, "Already have an error?"); 1.163 + 1.164 + mError = new DOMError(GetOwner(), aError); 1.165 + 1.166 + // Do the state transitions 1.167 + ChangeStateInternal(nsITelephonyProvider::CALL_STATE_DISCONNECTED, true); 1.168 + 1.169 + nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("error"), this); 1.170 + if (NS_FAILED(rv)) { 1.171 + NS_WARNING("Failed to dispatch error event!"); 1.172 + } 1.173 +} 1.174 + 1.175 +void 1.176 +TelephonyCall::ChangeGroup(TelephonyCallGroup* aGroup) 1.177 +{ 1.178 + mGroup = aGroup; 1.179 + 1.180 + nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("groupchange"), this); 1.181 + if (NS_FAILED(rv)) { 1.182 + NS_WARNING("Failed to dispatch error event!"); 1.183 + } 1.184 +} 1.185 + 1.186 +NS_IMPL_CYCLE_COLLECTION_INHERITED(TelephonyCall, 1.187 + DOMEventTargetHelper, 1.188 + mTelephony, 1.189 + mError, 1.190 + mGroup); 1.191 + 1.192 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TelephonyCall) 1.193 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.194 + 1.195 +NS_IMPL_ADDREF_INHERITED(TelephonyCall, DOMEventTargetHelper) 1.196 +NS_IMPL_RELEASE_INHERITED(TelephonyCall, DOMEventTargetHelper) 1.197 + 1.198 +// TelephonyCall WebIDL 1.199 + 1.200 +already_AddRefed<DOMError> 1.201 +TelephonyCall::GetError() const 1.202 +{ 1.203 + nsRefPtr<DOMError> error = mError; 1.204 + return error.forget(); 1.205 +} 1.206 + 1.207 +already_AddRefed<TelephonyCallGroup> 1.208 +TelephonyCall::GetGroup() const 1.209 +{ 1.210 + nsRefPtr<TelephonyCallGroup> group = mGroup; 1.211 + return group.forget(); 1.212 +} 1.213 + 1.214 +void 1.215 +TelephonyCall::Answer(ErrorResult& aRv) 1.216 +{ 1.217 + if (mCallState != nsITelephonyProvider::CALL_STATE_INCOMING) { 1.218 + NS_WARNING("Answer on non-incoming call ignored!"); 1.219 + return; 1.220 + } 1.221 + 1.222 + nsresult rv = mTelephony->Provider()->AnswerCall(mServiceId, mCallIndex); 1.223 + if (NS_FAILED(rv)) { 1.224 + aRv.Throw(rv); 1.225 + return; 1.226 + } 1.227 + 1.228 + ChangeStateInternal(nsITelephonyProvider::CALL_STATE_CONNECTING, true); 1.229 +} 1.230 + 1.231 +void 1.232 +TelephonyCall::HangUp(ErrorResult& aRv) 1.233 +{ 1.234 + if (mCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTING || 1.235 + mCallState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) { 1.236 + NS_WARNING("HangUp on previously disconnected call ignored!"); 1.237 + return; 1.238 + } 1.239 + 1.240 + nsresult rv = mCallState == nsITelephonyProvider::CALL_STATE_INCOMING ? 1.241 + mTelephony->Provider()->RejectCall(mServiceId, mCallIndex) : 1.242 + mTelephony->Provider()->HangUp(mServiceId, mCallIndex); 1.243 + if (NS_FAILED(rv)) { 1.244 + aRv.Throw(rv); 1.245 + return; 1.246 + } 1.247 + 1.248 + ChangeStateInternal(nsITelephonyProvider::CALL_STATE_DISCONNECTING, true); 1.249 +} 1.250 + 1.251 +void 1.252 +TelephonyCall::Hold(ErrorResult& aRv) 1.253 +{ 1.254 + if (mCallState != nsITelephonyProvider::CALL_STATE_CONNECTED) { 1.255 + NS_WARNING("Hold non-connected call ignored!"); 1.256 + return; 1.257 + } 1.258 + 1.259 + if (mGroup) { 1.260 + NS_WARNING("Hold a call in conference ignored!"); 1.261 + return; 1.262 + } 1.263 + 1.264 + if (!mSwitchable) { 1.265 + NS_WARNING("Hold a non-switchable call ignored!"); 1.266 + return; 1.267 + } 1.268 + 1.269 + nsresult rv = mTelephony->Provider()->HoldCall(mServiceId, mCallIndex); 1.270 + if (NS_FAILED(rv)) { 1.271 + aRv.Throw(rv); 1.272 + return; 1.273 + } 1.274 + 1.275 + if (!mSecondNumber.IsEmpty()) { 1.276 + // No state transition when we switch two numbers within one TelephonyCall 1.277 + // object. Otherwise, the state here will be inconsistent with the backend 1.278 + // RIL and will never be right. 1.279 + return; 1.280 + } 1.281 + 1.282 + ChangeStateInternal(nsITelephonyProvider::CALL_STATE_HOLDING, true); 1.283 +} 1.284 + 1.285 +void 1.286 +TelephonyCall::Resume(ErrorResult& aRv) 1.287 +{ 1.288 + if (mCallState != nsITelephonyProvider::CALL_STATE_HELD) { 1.289 + NS_WARNING("Resume non-held call ignored!"); 1.290 + return; 1.291 + } 1.292 + 1.293 + if (mGroup) { 1.294 + NS_WARNING("Resume a call in conference ignored!"); 1.295 + return; 1.296 + } 1.297 + 1.298 + if (!mSwitchable) { 1.299 + NS_WARNING("Resume a non-switchable call ignored!"); 1.300 + return; 1.301 + } 1.302 + 1.303 + nsresult rv = mTelephony->Provider()->ResumeCall(mServiceId, mCallIndex); 1.304 + if (NS_FAILED(rv)) { 1.305 + aRv.Throw(rv); 1.306 + return; 1.307 + } 1.308 + 1.309 + ChangeStateInternal(nsITelephonyProvider::CALL_STATE_RESUMING, true); 1.310 +}