--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -2636,32 +2636,58 @@ RilObject.prototype = {
}
// If the MMI code is not a known code, it is treated as an ussd.
if (!_isRadioAvailable()) {
return;
}
options.ussd = mmi.fullMMI;
+
+ if (options.startNewSession && this._ussdSession) {
+ if (DEBUG) this.context.debug("Cancel existing ussd session.");
+ this.cachedUSSDRequest = options;
+ this.cancelUSSD({});
+ return;
+ }
+
this.sendUSSD(options);
},
/**
+ * Cache the request for send out a new ussd when there is an existing
+ * session. We should do cancelUSSD first.
+ */
+ cachedUSSDRequest : null,
+
+ /**
* Send USSD.
*
* @param ussd
* String containing the USSD code.
- *
- */
- sendUSSD: function(options) {
- let Buf = this.context.Buf;
- Buf.newParcel(REQUEST_SEND_USSD, options);
- Buf.writeString(options.ussd);
- Buf.sendParcel();
- },
+ * @param checkSession
+ * True if an existing session should be there.
+ */
+ sendUSSD: function(options) {
+ if (options.checkSession && !this._ussdSession) {
+ options.success = false;
+ options.errorMsg = GECKO_ERROR_GENERIC_FAILURE;
+ this.sendChromeMessage(options);
+ return;
+ }
+
+ this.sendRilRequestSendUSSD(options);
+ },
+
+ sendRilRequestSendUSSD: function(options) {
+ let Buf = this.context.Buf;
+ Buf.newParcel(REQUEST_SEND_USSD, options);
+ Buf.writeString(options.ussd);
+ Buf.sendParcel();
+ },
/**
* Cancel pending USSD.
*/
cancelUSSD: function(options) {
this.context.Buf.simpleRequest(REQUEST_CANCEL_USSD, options);
},
@@ -5674,19 +5700,29 @@ RilObject.prototype[REQUEST_SEND_USSD] =
options.success = (this._ussdSession = options.rilRequestError === 0);
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.sendChromeMessage(options);
};
RilObject.prototype[REQUEST_CANCEL_USSD] = function REQUEST_CANCEL_USSD(length, options) {
if (DEBUG) {
this.context.debug("REQUEST_CANCEL_USSD" + JSON.stringify(options));
}
+
options.success = (options.rilRequestError === 0);
this._ussdSession = !options.success;
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
+
+ // The cancelUSSD is triggered by ril_worker itself.
+ if (this.cachedUSSDRequest) {
+ if (DEBUG) this.context.debug("Send out the cached ussd request");
+ this.sendUSSD(this.cachedUSSDRequest);
+ this.cachedUSSDRequest = null;
+ return;
+ }
+
this.sendChromeMessage(options);
};
RilObject.prototype[REQUEST_GET_CLIR] = function REQUEST_GET_CLIR(length, options) {
options.success = (options.rilRequestError === 0);
if (!options.success) {
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.sendChromeMessage(options);
return;
new file mode 100644
--- /dev/null
+++ b/dom/telephony/USSDSession.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/USSDSession.h"
+
+#include "mozilla/dom/USSDSessionBinding.h"
+#include "mozilla/dom/telephony/TelephonyCallback.h"
+#include "nsIGlobalObject.h"
+#include "nsServiceManagerUtils.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::dom::telephony;
+using mozilla::ErrorResult;
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(USSDSession, mWindow)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(USSDSession)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(USSDSession)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(USSDSession)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+USSDSession::USSDSession(nsPIDOMWindow* aWindow, nsITelephonyService* aService,
+ uint32_t aServiceId)
+ : mWindow(aWindow), mService(aService), mServiceId(aServiceId)
+{
+}
+
+USSDSession::~USSDSession()
+{
+}
+
+nsPIDOMWindow*
+USSDSession::GetParentObject() const
+{
+ return mWindow;
+}
+
+JSObject*
+USSDSession::WrapObject(JSContext* aCx)
+{
+ return USSDSessionBinding::Wrap(aCx, this);
+}
+
+// WebIDL
+
+already_AddRefed<USSDSession>
+USSDSession::Constructor(const GlobalObject& aGlobal, uint32_t aServiceId,
+ ErrorResult& aRv)
+{
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
+ if (!window) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return nullptr;
+ }
+
+ nsCOMPtr<nsITelephonyService> ril =
+ do_GetService(TELEPHONY_SERVICE_CONTRACTID);
+ if (!ril) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return nullptr;
+ }
+
+ nsRefPtr<USSDSession> session = new USSDSession(window, ril, aServiceId);
+ return session.forget();
+}
+
+already_AddRefed<Promise>
+USSDSession::Send(const nsAString& aUssd, ErrorResult& aRv)
+{
+ if (!mService) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
+ if (!global) {
+ return nullptr;
+ }
+
+ nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
+
+ nsresult rv = mService->SendUSSD(mServiceId, aUssd, callback);
+ if (NS_FAILED(rv)) {
+ promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+ }
+
+ return promise.forget();
+}
new file mode 100644
--- /dev/null
+++ b/dom/telephony/USSDSession.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_USSDSession_h
+#define mozilla_dom_USSDSession_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/Promise.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsITelephonyService.h"
+#include "nsPIDOMWindow.h"
+#include "nsWrapperCache.h"
+
+struct JSContext;
+
+namespace mozilla {
+namespace dom {
+
+class USSDSession MOZ_FINAL : public nsISupports,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(USSDSession)
+
+ USSDSession(nsPIDOMWindow* aWindow, nsITelephonyService* aService,
+ uint32_t aServiceId);
+
+ nsPIDOMWindow*
+ GetParentObject() const;
+
+ virtual JSObject*
+ WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+ // WebIDL
+ static already_AddRefed<USSDSession>
+ Constructor(const GlobalObject& aGlobal, uint32_t aServiceId,
+ ErrorResult& aRv);
+
+ already_AddRefed<Promise>
+ Send(const nsAString& aUssd, ErrorResult& aRv);
+
+private:
+ ~USSDSession();
+
+ nsCOMPtr<nsPIDOMWindow> mWindow;
+ nsCOMPtr<nsITelephonyService> mService;
+ uint32_t mServiceId;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_USSDSession_h
--- a/dom/telephony/gonk/TelephonyService.js
+++ b/dom/telephony/gonk/TelephonyService.js
@@ -580,17 +580,17 @@ TelephonyService.prototype = {
isDialEmergency: aIsDialEmergency }, aCallback);
} else {
// Reject MMI code from dialEmergency api.
if (aIsDialEmergency) {
aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
return;
}
- this._dialMMI(aClientId, mmi, aCallback);
+ this._dialMMI(aClientId, mmi, aCallback, true);
}
},
/**
* @param aOptions.number
* @param aOptions.clirMode (optional)
* @param aOptions.isDialEmergency
*/
@@ -668,26 +668,34 @@ TelephonyService.prototype = {
// RIL doesn't hold the 2nd call. We create one by ourselves.
aCallback.notifyDialCallSuccess(CDMA_SECOND_CALL_INDEX, response.number);
this._addCdmaChildCall(aClientId, response.number, currentCdmaCallIndex);
}
});
},
/**
+ * @param aClientId
+ * Client id.
* @param aMmi
* Parsed MMI structure.
+ * @param aCallback
+ * A nsITelephonyDialCallback object.
+ * @param aStartNewSession
+ * True to start a new session for ussd request.
*/
- _dialMMI: function(aClientId, aMmi, aCallback) {
+ _dialMMI: function(aClientId, aMmi, aCallback, aStartNewSession) {
let mmiServiceCode = aMmi ?
this._serviceCodeToKeyString(aMmi.serviceCode) : RIL.MMI_KS_SC_USSD;
aCallback.notifyDialMMI(mmiServiceCode);
- this._sendToRilWorker(aClientId, "sendMMI", { mmi: aMmi }, response => {
+ this._sendToRilWorker(aClientId, "sendMMI",
+ { mmi: aMmi,
+ startNewSession: aStartNewSession }, response => {
if (DEBUG) debug("MMI response: " + JSON.stringify(response));
if (!response.success) {
if (response.additionalInformation != null) {
aCallback.notifyDialMMIErrorWithInfo(response.errorMsg,
response.additionalInformation);
} else {
aCallback.notifyDialMMIError(response.errorMsg);
@@ -1043,16 +1051,28 @@ TelephonyService.prototype = {
holdConference: function(aClientId) {
this._sendToRilWorker(aClientId, "holdConference");
},
resumeConference: function(aClientId) {
this._sendToRilWorker(aClientId, "resumeConference");
},
+ sendUSSD: function(aClientId, aUssd, aCallback) {
+ this._sendToRilWorker(aClientId, "sendUSSD",
+ { ussd: aUssd, checkSession: true },
+ response => {
+ if (!response.success) {
+ aCallback.notifyError(response.errorMsg);
+ } else {
+ aCallback.notifySuccess();
+ }
+ });
+ },
+
get microphoneMuted() {
return gAudioManager.microphoneMuted;
},
set microphoneMuted(aMuted) {
if (aMuted == this.microphoneMuted) {
return;
}
@@ -1263,17 +1283,17 @@ TelephonyService.prototype = {
notifyConferenceCallStateChanged: function(aState) {
if (DEBUG) debug("handleConferenceCallStateChanged: " + aState);
aState = this._convertRILCallState(aState);
this._notifyAllListeners("conferenceCallStateChanged", [aState]);
},
dialMMI: function(aClientId, aMmiString, aCallback) {
let mmi = this._parseMMI(aMmiString, this._hasCalls(aClientId));
- this._dialMMI(aClientId, mmi, aCallback);
+ this._dialMMI(aClientId, mmi, aCallback, false);
},
/**
* nsIObserver interface.
*/
observe: function(aSubject, aTopic, aData) {
switch (aTopic) {
--- a/dom/telephony/ipc/PTelephony.ipdl
+++ b/dom/telephony/ipc/PTelephony.ipdl
@@ -19,20 +19,27 @@ struct EnumerateCallsRequest
struct DialRequest
{
uint32_t clientId;
nsString number;
bool isEmergency;
};
+struct USSDRequest
+{
+ uint32_t clientId;
+ nsString ussd;
+};
+
union IPCTelephonyRequest
{
EnumerateCallsRequest;
DialRequest;
+ USSDRequest;
};
sync protocol PTelephony {
manager PContent;
manages PTelephonyRequest;
child:
NotifyCallError(uint32_t aClientId, int32_t aCallIndex, nsString aError);
--- a/dom/telephony/ipc/TelephonyIPCService.cpp
+++ b/dom/telephony/ipc/TelephonyIPCService.cpp
@@ -297,16 +297,24 @@ TelephonyIPCService::StopTone(uint32_t a
return NS_ERROR_FAILURE;
}
mPTelephonyChild->SendStopTone(aClientId);
return NS_OK;
}
NS_IMETHODIMP
+TelephonyIPCService::SendUSSD(uint32_t aClientId, const nsAString& aUssd,
+ nsITelephonyCallback *aCallback)
+{
+ return SendRequest(nullptr, aCallback,
+ USSDRequest(aClientId, nsString(aUssd)));
+}
+
+NS_IMETHODIMP
TelephonyIPCService::GetMicrophoneMuted(bool* aMuted)
{
if (!mPTelephonyChild) {
NS_WARNING("TelephonyService used after shutdown has begun!");
return NS_ERROR_FAILURE;
}
mPTelephonyChild->SendGetMicrophoneMuted(aMuted);
--- a/dom/telephony/ipc/TelephonyParent.cpp
+++ b/dom/telephony/ipc/TelephonyParent.cpp
@@ -38,16 +38,18 @@ TelephonyParent::RecvPTelephonyRequestCo
{
TelephonyRequestParent* actor = static_cast<TelephonyRequestParent*>(aActor);
switch (aRequest.type()) {
case IPCTelephonyRequest::TEnumerateCallsRequest:
return actor->DoRequest(aRequest.get_EnumerateCallsRequest());
case IPCTelephonyRequest::TDialRequest:
return actor->DoRequest(aRequest.get_DialRequest());
+ case IPCTelephonyRequest::TUSSDRequest:
+ return actor->DoRequest(aRequest.get_USSDRequest());
default:
MOZ_CRASH("Unknown type!");
}
return false;
}
PTelephonyRequestParent*
@@ -430,16 +432,30 @@ TelephonyRequestParent::DoRequest(const
aRequest.isEmergency(), this);
} else {
return NS_SUCCEEDED(NotifyError(NS_LITERAL_STRING("InvalidStateError")));
}
return true;
}
+bool
+TelephonyRequestParent::DoRequest(const USSDRequest& aRequest)
+{
+ nsCOMPtr<nsITelephonyService> service =
+ do_GetService(TELEPHONY_SERVICE_CONTRACTID);
+ if (service) {
+ service->SendUSSD(aRequest.clientId(), aRequest.ussd(), this);
+ } else {
+ return NS_SUCCEEDED(NotifyError(NS_LITERAL_STRING("InvalidStateError")));
+ }
+
+ return true;
+}
+
nsresult
TelephonyRequestParent::SendResponse(const IPCTelephonyResponse& aResponse)
{
NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
return Send__delete__(this, aResponse) ? NS_OK : NS_ERROR_FAILURE;
}
--- a/dom/telephony/ipc/TelephonyParent.h
+++ b/dom/telephony/ipc/TelephonyParent.h
@@ -121,13 +121,16 @@ protected:
private:
bool mActorDestroyed;
bool
DoRequest(const EnumerateCallsRequest& aRequest);
bool
DoRequest(const DialRequest& aRequest);
+
+ bool
+ DoRequest(const USSDRequest& aRequest);
};
END_TELEPHONY_NAMESPACE
#endif /* mozilla_dom_telephony_TelephonyParent_h */
--- a/dom/telephony/moz.build
+++ b/dom/telephony/moz.build
@@ -11,16 +11,17 @@ XPIDL_SOURCES += [
XPIDL_MODULE = 'dom_telephony'
EXPORTS.mozilla.dom += [
'CallsList.h',
'Telephony.h',
'TelephonyCall.h',
'TelephonyCallGroup.h',
'TelephonyCallId.h',
+ 'USSDSession.h'
]
EXPORTS.mozilla.dom.telephony += [
'ipc/TelephonyChild.h',
'ipc/TelephonyParent.h',
'TelephonyCallback.h',
'TelephonyCommon.h',
'TelephonyDialCallback.h',
@@ -32,16 +33,17 @@ UNIFIED_SOURCES += [
'ipc/TelephonyIPCService.cpp',
'ipc/TelephonyParent.cpp',
'Telephony.cpp',
'TelephonyCall.cpp',
'TelephonyCallback.cpp',
'TelephonyCallGroup.cpp',
'TelephonyCallId.cpp',
'TelephonyDialCallback.cpp',
+ 'USSDSession.cpp',
]
IPDL_SOURCES += [
'ipc/PTelephony.ipdl',
'ipc/PTelephonyRequest.ipdl',
'ipc/TelephonyTypes.ipdlh'
]
--- a/dom/telephony/nsITelephonyService.idl
+++ b/dom/telephony/nsITelephonyService.idl
@@ -235,17 +235,17 @@ interface nsITelephonyDialCallback : nsI
#define TELEPHONY_SERVICE_CONTRACTID \
"@mozilla.org/telephony/telephonyservice;1"
%}
/**
* XPCOM component (in the content process) that provides the telephony
* information.
*/
-[scriptable, uuid(600929fb-269c-4ae2-a61f-3c8a62cf5456)]
+[scriptable, uuid(79188caa-046a-48e1-b9c5-2e891504dc7a)]
interface nsITelephonyService : nsISupports
{
const unsigned short CALL_STATE_UNKNOWN = 0;
const unsigned short CALL_STATE_DIALING = 1;
const unsigned short CALL_STATE_ALERTING = 2;
const unsigned short CALL_STATE_CONNECTING = 3;
const unsigned short CALL_STATE_CONNECTED = 4;
const unsigned short CALL_STATE_HOLDING = 5;
@@ -294,16 +294,26 @@ interface nsITelephonyService : nsISuppo
void holdCall(in unsigned long clientId, in unsigned long callIndex);
void resumeCall(in unsigned long clientId, in unsigned long callIndex);
void conferenceCall(in unsigned long clientId);
void separateCall(in unsigned long clientId, in unsigned long callIndex);
void holdConference(in unsigned long clientId);
void resumeConference(in unsigned long clientId);
+ /**
+ * Send an USSD on existing session. It results in error if the session is
+ * not existed.
+ *
+ * If successful, callback.notifySuccess() will be called.
+ * Otherwise, callback.notifyError() will be called.
+ */
+ void sendUSSD(in unsigned long clientId, in DOMString ussd,
+ in nsITelephonyCallback callback);
+
attribute bool microphoneMuted;
attribute bool speakerEnabled;
};
%{C++
template<typename T> struct already_AddRefed;
already_AddRefed<nsITelephonyService>