Bug 1129882 - Create agent in telephony object. r=szchen, r=htsai

This commit is contained in:
Alastor Wu 2015-03-26 16:27:25 +08:00
parent c90fd54e1c
commit 6f94054c03
7 changed files with 265 additions and 59 deletions

View File

@ -7,6 +7,7 @@
#include "Telephony.h" #include "Telephony.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/dom/AudioChannelBinding.h"
#include "mozilla/dom/CallEvent.h" #include "mozilla/dom/CallEvent.h"
#include "mozilla/dom/MozMobileConnectionBinding.h" #include "mozilla/dom/MozMobileConnectionBinding.h"
#include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise.h"
@ -20,6 +21,7 @@
#include "nsServiceManagerUtils.h" #include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "AudioChannelAgent.h"
#include "CallsList.h" #include "CallsList.h"
#include "TelephonyCall.h" #include "TelephonyCall.h"
#include "TelephonyCallGroup.h" #include "TelephonyCallGroup.h"
@ -62,8 +64,11 @@ public:
}; };
Telephony::Telephony(nsPIDOMWindow* aOwner) Telephony::Telephony(nsPIDOMWindow* aOwner)
: DOMEventTargetHelper(aOwner) : DOMEventTargetHelper(aOwner),
mIsAudioStartPlaying(false),
mAudioAgentNotify(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY)
{ {
MOZ_ASSERT(aOwner);
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aOwner); nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aOwner);
MOZ_ASSERT(global); MOZ_ASSERT(global);
@ -518,6 +523,61 @@ Telephony::StopTone(const Optional<uint32_t>& aServiceId, ErrorResult& aRv)
aRv = mService->StopTone(serviceId); aRv = mService->StopTone(serviceId);
} }
void
Telephony::OwnAudioChannel(ErrorResult& aRv)
{
if (mAudioAgent) {
return;
}
mAudioAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
MOZ_ASSERT(mAudioAgent);
aRv = mAudioAgent->Init(GetParentObject(),
(int32_t)AudioChannel::Telephony, this);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
aRv = HandleAudioAgentState();
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
nsresult
Telephony::HandleAudioAgentState()
{
if (!mAudioAgent) {
return NS_OK;
}
Nullable<OwningTelephonyCallOrTelephonyCallGroup> activeCall;
GetActive(activeCall);
nsresult rv;
// Only stop agent when the call is disconnected.
if ((!mCalls.Length() && !mGroup->CallsArray().Length()) &&
mIsAudioStartPlaying) {
mIsAudioStartPlaying = false;
rv = mAudioAgent->NotifyStoppedPlaying(mAudioAgentNotify);
mAudioAgent = nullptr;
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else if (!activeCall.IsNull() && !mIsAudioStartPlaying) {
mIsAudioStartPlaying = true;
float volume = 1.0;
bool muted = false;
rv = mAudioAgent->NotifyStartedPlaying(mAudioAgentNotify, &volume, &muted);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = WindowVolumeChanged(volume, muted);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
}
bool bool
Telephony::GetMuted(ErrorResult& aRv) const Telephony::GetMuted(ErrorResult& aRv) const
{ {
@ -591,13 +651,62 @@ Telephony::GetReady(ErrorResult& aRv) const
return promise.forget(); return promise.forget();
} }
// nsIAudioChannelAgentCallback
NS_IMETHODIMP
Telephony::WindowVolumeChanged(float aVolume, bool aMuted)
{
// Check the limitation of the network connection
if (mCalls.Length() > 1 ||
(mCalls.Length() == 1 && mGroup->CallsArray().Length())) {
return NS_ERROR_FAILURE;
}
ErrorResult rv;
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
nsRefPtr<Promise> promise = Promise::Create(global, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
// Check the single call or conference call
bool isSingleCall = mCalls.Length();
nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
if (isSingleCall) {
rv = aMuted ? mCalls[0]->Hold(callback) : mCalls[0]->Resume(callback);
} else {
rv = aMuted ? mGroup->Hold(callback) : mGroup->Resume(callback);
}
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
return NS_OK;
}
NS_IMETHODIMP
Telephony::WindowAudioCaptureChanged()
{
// Do nothing
return NS_OK;
}
// nsITelephonyListener // nsITelephonyListener
NS_IMETHODIMP NS_IMETHODIMP
Telephony::CallStateChanged(uint32_t aLength, nsITelephonyCallInfo** aAllInfo) Telephony::CallStateChanged(uint32_t aLength, nsITelephonyCallInfo** aAllInfo)
{ {
nsresult rv;
for (uint32_t i = 0; i < aLength; ++i) { for (uint32_t i = 0; i < aLength; ++i) {
HandleCallInfo(aAllInfo[i]); rv = HandleCallInfo(aAllInfo[i]);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
rv = HandleAudioAgentState();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
} }
return NS_OK; return NS_OK;
} }
@ -605,7 +714,8 @@ Telephony::CallStateChanged(uint32_t aLength, nsITelephonyCallInfo** aAllInfo)
NS_IMETHODIMP NS_IMETHODIMP
Telephony::EnumerateCallState(nsITelephonyCallInfo* aInfo) Telephony::EnumerateCallState(nsITelephonyCallInfo* aInfo)
{ {
return HandleCallInfo(aInfo); uint32_t currentCallNum = 1;
return CallStateChanged(currentCallNum, &aInfo);
} }
NS_IMETHODIMP NS_IMETHODIMP

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise.h"
#include "mozilla/dom/telephony/TelephonyCommon.h" #include "mozilla/dom/telephony/TelephonyCommon.h"
#include "nsIAudioChannelAgent.h"
#include "nsITelephonyCallInfo.h" #include "nsITelephonyCallInfo.h"
#include "nsITelephonyService.h" #include "nsITelephonyService.h"
@ -31,6 +32,7 @@ class TelephonyDialCallback;
class OwningTelephonyCallOrTelephonyCallGroup; class OwningTelephonyCallOrTelephonyCallGroup;
class Telephony final : public DOMEventTargetHelper, class Telephony final : public DOMEventTargetHelper,
public nsIAudioChannelAgentCallback,
private nsITelephonyListener private nsITelephonyListener
{ {
/** /**
@ -44,6 +46,8 @@ class Telephony final : public DOMEventTargetHelper,
friend class telephony::TelephonyDialCallback; friend class telephony::TelephonyDialCallback;
// The audio agent is needed to communicate with the audio channel service.
nsCOMPtr<nsIAudioChannelAgent> mAudioAgent;
nsCOMPtr<nsITelephonyService> mService; nsCOMPtr<nsITelephonyService> mService;
nsRefPtr<Listener> mListener; nsRefPtr<Listener> mListener;
@ -54,8 +58,13 @@ class Telephony final : public DOMEventTargetHelper,
nsRefPtr<Promise> mReadyPromise; nsRefPtr<Promise> mReadyPromise;
bool mIsAudioStartPlaying;
uint32_t mAudioAgentNotify;
public: public:
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
NS_DECL_NSITELEPHONYLISTENER NS_DECL_NSITELEPHONYLISTENER
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper) NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Telephony, NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Telephony,
@ -94,6 +103,15 @@ public:
void void
StopTone(const Optional<uint32_t>& aServiceId, ErrorResult& aRv); StopTone(const Optional<uint32_t>& aServiceId, ErrorResult& aRv);
// In the audio channel architecture, the system app needs to know the state
// of every audio channel, including the telephony. Therefore, when a
// telephony call is activated , the audio channel service would notify the
// system app about that. And we need a agent to communicate with the audio
// channel service. We would follow the call states to make a correct
// notification.
void
OwnAudioChannel(ErrorResult& aRv);
bool bool
GetMuted(ErrorResult& aRv) const; GetMuted(ErrorResult& aRv) const;
@ -213,6 +231,10 @@ private:
nsresult nsresult
HandleCallInfo(nsITelephonyCallInfo* aInfo); HandleCallInfo(nsITelephonyCallInfo* aInfo);
// Check the call states to decide whether need to send the notificaiton.
nsresult
HandleAudioAgentState();
}; };
} // namespace dom } // namespace dom

View File

@ -324,34 +324,10 @@ TelephonyCall::Hold(ErrorResult& aRv)
return nullptr; return nullptr;
} }
if (mCallState != nsITelephonyService::CALL_STATE_CONNECTED) {
NS_WARNING(nsPrintfCString("Hold non-connected call is rejected!"
" (State: %u)", mCallState).get());
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
if (mGroup) {
NS_WARNING("Hold a call in conference is rejected!");
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
if (!mSwitchable) {
NS_WARNING("Hold a non-switchable call is rejected!");
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise); nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
aRv = mTelephony->Service()->HoldCall(mServiceId, mCallIndex, callback); aRv = Hold(callback);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr); if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
if (mSecondId) {
// No state transition when we switch two numbers within one TelephonyCall
// object. Otherwise, the state here will be inconsistent with the backend
// RIL and will never be right.
return promise.forget();
} }
return promise.forget(); return promise.forget();
@ -365,28 +341,77 @@ TelephonyCall::Resume(ErrorResult& aRv)
return nullptr; return nullptr;
} }
if (mCallState != nsITelephonyService::CALL_STATE_HELD) { nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
NS_WARNING(nsPrintfCString("Resume non-held call is rejected!" aRv = Resume(callback);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return promise.forget();
}
nsresult
TelephonyCall::Hold(nsITelephonyCallback* aCallback)
{
if (mCallState != nsITelephonyService::CALL_STATE_CONNECTED) {
NS_WARNING(nsPrintfCString("Hold non-connected call is rejected!"
" (State: %u)", mCallState).get()); " (State: %u)", mCallState).get());
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
return promise.forget(); return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (mGroup) {
NS_WARNING("Hold a call in conference is rejected!");
aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (!mSwitchable) {
NS_WARNING("Hold a non-switchable call is rejected!");
aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsresult rv = mTelephony->Service()->HoldCall(mServiceId, mCallIndex, aCallback);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (mSecondId) {
// No state transition when we switch two numbers within one TelephonyCall
// object. Otherwise, the state here will be inconsistent with the backend
// RIL and will never be right.
return NS_OK;
}
return NS_OK;
}
nsresult
TelephonyCall::Resume(nsITelephonyCallback* aCallback)
{
if (mCallState != nsITelephonyService::CALL_STATE_HELD) {
NS_WARNING("Resume non-held call is rejected!");
aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
return NS_ERROR_DOM_INVALID_STATE_ERR;
} }
if (mGroup) { if (mGroup) {
NS_WARNING("Resume a call in conference is rejected!"); NS_WARNING("Resume a call in conference is rejected!");
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
return promise.forget(); return NS_ERROR_DOM_INVALID_STATE_ERR;
} }
if (!mSwitchable) { if (!mSwitchable) {
NS_WARNING("Resume a non-switchable call is rejected!"); NS_WARNING("Resume a non-switchable call is rejected!");
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
return promise.forget(); return NS_ERROR_DOM_INVALID_STATE_ERR;
} }
nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise); nsresult rv = mTelephony->Service()->ResumeCall(mServiceId, mCallIndex, aCallback);
aRv = mTelephony->Service()->ResumeCall(mServiceId, mCallIndex, callback); if (NS_WARN_IF(NS_FAILED(rv))) {
NS_ENSURE_TRUE(!aRv.Failed(), nullptr); return rv;
}
return promise.forget(); return NS_OK;
} }

View File

@ -12,6 +12,7 @@
#include "mozilla/dom/TelephonyCallBinding.h" #include "mozilla/dom/TelephonyCallBinding.h"
#include "mozilla/dom/TelephonyCallId.h" #include "mozilla/dom/TelephonyCallId.h"
#include "mozilla/dom/telephony/TelephonyCommon.h" #include "mozilla/dom/telephony/TelephonyCommon.h"
#include "nsITelephonyService.h"
class nsPIDOMWindow; class nsPIDOMWindow;
@ -185,6 +186,12 @@ private:
~TelephonyCall(); ~TelephonyCall();
nsresult
Hold(nsITelephonyCallback* aCallback);
nsresult
Resume(nsITelephonyCallback* aCallback);
void void
ChangeStateInternal(uint16_t aCallState, bool aFireEvents); ChangeStateInternal(uint16_t aCallState, bool aFireEvents);

View File

@ -347,16 +347,12 @@ TelephonyCallGroup::Hold(ErrorResult& aRv)
return nullptr; return nullptr;
} }
if (mCallState != nsITelephonyService::CALL_STATE_CONNECTED) { nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
NS_WARNING("Holding a non-connected call is rejected!"); aRv = Hold(callback);
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); if (NS_WARN_IF(aRv.Failed())) {
return promise.forget(); return nullptr;
} }
nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
aRv = mTelephony->Service()->HoldConference(mCalls[0]->ServiceId(),
callback);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
return promise.forget(); return promise.forget();
} }
@ -370,15 +366,47 @@ TelephonyCallGroup::Resume(ErrorResult& aRv)
return nullptr; return nullptr;
} }
if (mCallState != nsITelephonyService::CALL_STATE_HELD) { nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
NS_WARNING("Resuming a non-held call is rejected!"); aRv = Resume(callback);
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); if (NS_WARN_IF(aRv.Failed())) {
return promise.forget(); return nullptr;
} }
nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
aRv = mTelephony->Service()->ResumeConference(mCalls[0]->ServiceId(),
callback);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
return promise.forget(); return promise.forget();
} }
nsresult
TelephonyCallGroup::Hold(nsITelephonyCallback* aCallback)
{
if (mCallState != nsITelephonyService::CALL_STATE_CONNECTED) {
NS_WARNING("Holding a non-connected call is rejected!");
aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsresult rv = mTelephony->Service()->HoldConference(mCalls[0]->ServiceId(),
aCallback);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
TelephonyCallGroup::Resume(nsITelephonyCallback* aCallback)
{
if (mCallState != nsITelephonyService::CALL_STATE_HELD) {
NS_WARNING("Resuming a non-held call is rejected!");
aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError"));
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsresult rv = mTelephony->Service()->ResumeConference(mCalls[0]->ServiceId(),
aCallback);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}

View File

@ -30,6 +30,8 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TelephonyCallGroup, NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TelephonyCallGroup,
DOMEventTargetHelper) DOMEventTargetHelper)
friend class Telephony;
nsPIDOMWindow* nsPIDOMWindow*
GetParentObject() const GetParentObject() const
{ {
@ -108,6 +110,12 @@ private:
explicit TelephonyCallGroup(nsPIDOMWindow* aOwner); explicit TelephonyCallGroup(nsPIDOMWindow* aOwner);
~TelephonyCallGroup(); ~TelephonyCallGroup();
nsresult
Hold(nsITelephonyCallback* aCallback);
nsresult
Resume(nsITelephonyCallback* aCallback);
nsresult nsresult
NotifyCallsChanged(TelephonyCall* aCall); NotifyCallsChanged(TelephonyCall* aCall);

View File

@ -49,6 +49,12 @@ interface Telephony : EventTarget {
[Throws] [Throws]
void stopTone(optional unsigned long serviceId); void stopTone(optional unsigned long serviceId);
// Calling this method, the app will be treated as owner of the telephony
// calls from the AudioChannel policy.
[Throws,
CheckAllPermissions="audio-channel-telephony"]
void ownAudioChannel();
[Throws] [Throws]
attribute boolean muted; attribute boolean muted;