import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1140330 - Clear JSHint warning in RILContentHelper.js. r=hsinyi (e9d488f8f)
- Bug 1155142 - Part 1: Move All IccInfo-related Implementation to IccService to deprecate rilContext. r=echen (79c4edb86)
- Bug 1155142 - Part 2: Refactor RIL-related Modules. r=kchen (f98c9e4e6)
- Bug 1155142 - Part 3: Refactor MobileIdentityManager. r=ferjm (530b9a25e)
- Bug 1114901 - Part 1: introduce nsI(Gonk)DataCallInterfaceService and nsIDataCallManager. r=hsinyi,echen (4f941a2ba)
- Bug 1114901 - Part 2: (Gonk)DataCallInterfaceService implementation. r=echen (a7b965106)
- Bug 1044721 - Part 1: Add setSmscAddress API in MozMobileMessageManager Web IDL interface, and corresponding implementation in MobileMessageManager class. r=hsinyi (d75e7d34d)
- Bug 1044721 - Part 2: Update nsIMobileMessageCallback interface and implementation to support Promise and setSmscAddress. r=btseng (7bdae2eb7)
- Bug 1069186 - determine LTE signal level based on rsrp and rssnr. r=edgar (25c7ad339)
- Bug 1146799 - B2G RIL: Pull out the TelephonyRequestQueue from RilWorker. r=aknow (ed758026a)
- Bug 1130292 - Allow to receive WAP Push in which reserved port numbers is used. r=echen (71a163e0a)
- Bug 1139835 - Parse response parcel of OPEN_CHANNEL as int list. r=hsinyi (416cd7c43)
- Bug 1137088: B2G RIL: move data call related handling out of ril_worker. r=echen (221e01130)
- Bug 1027546 - Part 1: Restore the service class (ril_worker.js). r=aknow (5e0a9a4ae)
- Bug 1137093 - Part 01: Pass number instead of callIndex in notifySupplementaryService. r=hsinyi (f60cce375)
- Bug 1137093 - Part 02: Move from ril_worker to TelephonyService: hangup and reject. r=hsinyi (4f626592b)
- Bug 1137093 - Part 03: Move from ril_worker to TelephonyService: answer. r=hsinyi (046035101)
- Bug 1137093 - Part 04: Refactor _switchActiveCall. r=hsinyi (1da5886cb)
- Bug 1137093 - Part 05: Refactor _dialInCallMMI. r=hsinyi (f204a859c)
- Bug 1135268 - Part 1: Fix incall MMI issue. r=hsinyi (eec6f1d95)
- Bug 1135268 - Part 2: Test case. r=hsinyi (8dba4dd16)
- Bug 1137093 - Part 06: Refactor _isActive. r=hsinyi (bc469dffd)
- Bug 1137093 - Part 07: Use notifyCurrentCalls (idl). r=hsinyi (31d97ca81)
- Bug 1137093 - Part 08: Use notifyCurrentCalls (ril). r=hsinyi (120365c8a)
- Bug 1137093 - Part 09: Modify test case. r=hsinyi (6234e9069)
This commit is contained in:
2021-09-07 15:42:51 +08:00
parent 2efea470e5
commit f09fc5894b
41 changed files with 2139 additions and 2086 deletions
+2
View File
@@ -493,6 +493,8 @@
@RESPATH@/components/MobileMessageDatabaseService.js
@RESPATH@/components/MobileMessageDatabaseService.manifest
#ifndef DISABLE_MOZ_RIL_GEOLOC
@RESPATH@/components/DataCallInterfaceService.js
@RESPATH@/components/DataCallInterfaceService.manifest
@RESPATH@/components/MobileConnectionService.js
@RESPATH@/components/MobileConnectionService.manifest
@RESPATH@/components/RadioInterfaceLayer.js
+1 -10
View File
@@ -15,9 +15,6 @@
// Service instantiation
#include "ipc/IccIPCService.h"
#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
// TODO: Bug 815526, deprecate RILContentHelper.
#include "nsIRadioInterfaceLayer.h"
#include "nsRadioInterfaceLayer.h"
#include "nsIGonkIccService.h"
#endif
#include "nsXULAppAPI.h" // For XRE_GetProcessType()
@@ -150,13 +147,7 @@ NS_CreateIccService()
service = new mozilla::dom::icc::IccIPCService();
#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
} else {
// TODO: Bug 815526, deprecate RILContentHelper.
nsCOMPtr <nsIRadioInterfaceLayer> ril =
do_GetService(NS_RADIOINTERFACELAYER_CONTRACTID);
nsCOMPtr <nsIRadioInterfaceLayer_new> ril_new(do_QueryInterface(ril));
service = (ril_new) ? do_GetService(GONK_ICC_SERVICE_CONTRACTID)
: do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
service = do_GetService(GONK_ICC_SERVICE_CONTRACTID);
#endif
}
+57 -6
View File
@@ -25,6 +25,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
"@mozilla.org/ril;1",
"nsIRadioInterfaceLayer");
XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
"nsIGonkMobileConnectionService");
let DEBUG = RIL.DEBUG_RIL;
function debug(s) {
dump("IccService: " + s);
@@ -73,7 +77,7 @@ function IccService() {
let numClients = gRadioInterfaceLayer.numRadioInterfaces;
for (let i = 0; i < numClients; i++) {
this._iccs.push(new Icc(gRadioInterfaceLayer.getRadioInterface(i)));
this._iccs.push(new Icc(i));
}
this._updateDebugFlag();
@@ -145,7 +149,7 @@ IccService.prototype = {
}
let icc = this.getIccByServiceId(aServiceId);
icc._imsi = aImsi;
icc.imsi = aImsi || null;
},
/**
@@ -166,15 +170,16 @@ IccService.prototype = {
}
};
function Icc(aRadioInterface) {
this._radioInterface = aRadioInterface;
function Icc(aClientId) {
this._clientId = aClientId;
this._radioInterface = gRadioInterfaceLayer.getRadioInterface(aClientId);
this._listeners = [];
}
Icc.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIIcc]),
_clientId: 0,
_radioInterface: null,
_imsi: null,
_listeners: null,
_updateCardState: function(aCardState) {
@@ -192,24 +197,52 @@ Icc.prototype = {
}
},
/**
* A utility function to compare objects. The srcInfo may contain
* "rilMessageType", should ignore it.
*/
_isInfoChanged: function(srcInfo, destInfo) {
if (!destInfo) {
return true;
}
for (let key in srcInfo) {
if (key === "rilMessageType") {
continue;
}
if (srcInfo[key] !== destInfo[key]) {
return true;
}
}
return false;
},
/**
* We need to consider below cases when update iccInfo:
* 1. Should clear iccInfo to null if there is no card detected.
* 2. Need to create corresponding object based on iccType.
*/
_updateIccInfo: function(aIccInfo) {
let oldSpn = this.iccInfo ? this.iccInfo.spn : null;
// Card is not detected, clear iccInfo to null.
if (!aIccInfo || !aIccInfo.iccid) {
if (this.iccInfo) {
if (DEBUG) {
debug("Card is not detected, clear iccInfo to null.");
}
this.imsi = null;
this.iccInfo = null;
this._deliverListenerEvent("notifyIccInfoChanged");
}
return;
}
if (!this._isInfoChanged(aIccInfo, this.iccInfo)) {
return;
}
// If iccInfo is null, new corresponding object based on iccType.
if (!this.iccInfo ||
this.iccInfo.iccType != aIccInfo.iccType) {
@@ -233,6 +266,23 @@ Icc.prototype = {
aIccInfo.mcc.toString());
} catch (e) {}
}
// Update lastKnownHomeNetwork.
if (aIccInfo.mcc && aIccInfo.mnc) {
let lastKnownHomeNetwork = aIccInfo.mcc + "-" + aIccInfo.mnc;
// Append spn information if available.
if (aIccInfo.spn) {
lastKnownHomeNetwork += "-" + aIccInfo.spn;
}
gMobileConnectionService.notifyLastHomeNetworkChanged(this._clientId,
lastKnownHomeNetwork);
}
// If spn becomes available, we should check roaming again.
if (!oldSpn && aIccInfo.spn) {
gMobileConnectionService.notifySpnAvailable(this._clientId);
}
},
_deliverListenerEvent: function(aName, aArgs) {
@@ -305,6 +355,7 @@ Icc.prototype = {
*/
iccInfo: null,
cardState: Ci.nsIIcc.CARD_STATE_UNKNOWN,
imsi: null,
registerListener: function(aListener) {
if (this._listeners.indexOf(aListener) >= 0) {
@@ -379,7 +430,7 @@ Icc.prototype = {
switch (aMvnoType) {
case Ci.nsIIcc.CARD_MVNO_TYPE_IMSI:
let imsi = this._imsi;
let imsi = this.imsi;
if (!imsi) {
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
break;
+6 -1
View File
@@ -97,7 +97,7 @@ NS_CreateIccService();
/**
* XPCOM component that provides the access to the selected ICC.
*/
[scriptable, uuid(38a5bbe2-add6-11e4-ba9e-e390d1d19195)]
[scriptable, uuid(20a99186-e4cb-11e4-a5f9-938abcf7c826)]
interface nsIIcc : nsISupports
{
/**
@@ -214,6 +214,11 @@ interface nsIIcc : nsISupports
*/
readonly attribute unsigned long cardState;
/**
* IMSI of this ICC.
*/
readonly attribute DOMString imsi;
/**
* Get the status of an ICC lock (e.g. the PIN lock).
*
+7
View File
@@ -184,6 +184,13 @@ IccChild::GetCardState(uint32_t* aCardState)
return NS_OK;
}
NS_IMETHODIMP
IccChild::GetImsi(nsAString & aImsi)
{
NS_WARNING("IMSI shall not directly be fetched in child process.");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
IccChild::GetCardLockEnabled(uint32_t aLockType,
nsIIccCallback* aRequestReply)
@@ -56,6 +56,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
"@mozilla.org/network/manager;1",
"nsINetworkManager");
XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
"@mozilla.org/icc/iccservice;1",
"nsIIccService");
XPCOMUtils.defineLazyGetter(this, "gRadioInterfaceLayer", function() {
let ril = { numRadioInterfaces: 0 };
try {
@@ -387,9 +391,8 @@ MobileConnectionProvider.prototype = {
* really the case. See bug 787967
*/
_checkRoamingBetweenOperators: function(aNetworkInfo) {
// TODO: Bug 864489 - B2G RIL: use ipdl as IPC in MozIccManager
// Should get iccInfo from GonkIccProvider.
let iccInfo = this._radioInterface.rilContext.iccInfo;
let icc = gIccService.getIccByServiceId(this._clientId);
let iccInfo = icc ? icc.iccInfo : null;
let operator = aNetworkInfo.network;
let state = aNetworkInfo.state;
@@ -20,18 +20,6 @@ const TEST_DATA = [
relSignalStrength: null
}
},
// Valid rxlev with max value.
{
input: {
rxlev: 63,
rsrp: 65535,
rssnr: 65535
},
expect: {
signalStrength: -48,
relSignalStrength: 100
}
},
// Valid rxlev.
{
input: {
@@ -40,7 +28,7 @@ const TEST_DATA = [
rssnr: 65535
},
expect: {
signalStrength: -99,
signalStrength: null,
relSignalStrength: 100
}
},
@@ -52,9 +40,57 @@ const TEST_DATA = [
rssnr: 65535
},
expect: {
signalStrength: -111,
signalStrength: null,
relSignalStrength: 0
}
},
// Valid rxlev with max value.
{
input: {
rxlev: 63,
rsrp: 65535,
rssnr: 65535
},
expect: {
signalStrength: null,
relSignalStrength: 100
}
},
// Valid rsrp.
{
input: {
rxlev: 31,
rsrp: 50,
rssnr: 65535
},
expect: {
signalStrength: 50,
relSignalStrength: 100
}
},
// Valid rssnr.
{
input: {
rxlev: 31,
rsrp: 65535,
rssnr: 100
},
expect: {
signalStrength: null,
relSignalStrength: 81
}
},
// Valid rsrp and rssnr.
{
input: {
rxlev: 31,
rsrp: 100,
rssnr: 30
},
expect: {
signalStrength: 100,
relSignalStrength: 37
}
}
];
@@ -18,6 +18,7 @@
#include "nsServiceManagerUtils.h"
#include "nsTArrayHelpers.h"
#include "DOMMobileMessageError.h"
#include "mozilla/dom/Promise.h"
namespace mozilla {
namespace dom {
@@ -81,6 +82,11 @@ MobileMessageCallback::MobileMessageCallback(DOMRequest* aDOMRequest)
{
}
MobileMessageCallback::MobileMessageCallback(Promise* aPromise)
: mPromise(aPromise)
{
}
MobileMessageCallback::~MobileMessageCallback()
{
}
@@ -281,6 +287,21 @@ MobileMessageCallback::NotifyGetSmscAddressFailed(int32_t aError)
return NotifyError(aError);
}
NS_IMETHODIMP
MobileMessageCallback::NotifySetSmscAddress()
{
mPromise->MaybeResolve(JS::UndefinedHandleValue);
return NS_OK;
}
NS_IMETHODIMP
MobileMessageCallback::NotifySetSmscAddressFailed(int32_t aError)
{
const nsAString& errorStr = ConvertErrorCodeToErrorString(aError);
mPromise->MaybeRejectBrokenly(errorStr);
return NS_OK;
}
} // namespace mobilemessage
} // namespace dom
} // namespace mozilla
@@ -11,6 +11,8 @@
#include "nsCOMPtr.h"
#include "DOMRequest.h"
class Promise;
namespace mozilla {
namespace dom {
namespace mobilemessage {
@@ -22,11 +24,13 @@ public:
NS_DECL_NSIMOBILEMESSAGECALLBACK
explicit MobileMessageCallback(DOMRequest* aDOMRequest);
explicit MobileMessageCallback(Promise* aPromise);
private:
~MobileMessageCallback();
nsRefPtr<DOMRequest> mDOMRequest;
nsRefPtr<Promise> mPromise;
nsresult NotifySuccess(JS::Handle<JS::Value> aResult, bool aAsync = false);
nsresult NotifySuccess(nsISupports *aMessage, bool aAsync = false);
@@ -16,6 +16,7 @@
#include "mozilla/dom/MozMmsEvent.h"
#include "mozilla/dom/MozMobileMessageManagerBinding.h"
#include "mozilla/dom/MozSmsEvent.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
@@ -704,6 +705,72 @@ MobileMessageManager::GetSmscAddress(const Optional<uint32_t>& aServiceId,
return request.forget();
}
already_AddRefed<Promise>
MobileMessageManager::SetSmscAddress(const SmscAddress& aSmscAddress,
const Optional<uint32_t>& aServiceId,
ErrorResult& aRv)
{
nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
if (!smsService) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
// Use the default one unless |serviceId| is available.
uint32_t serviceId;
nsresult rv;
if (aServiceId.WasPassed()) {
serviceId = aServiceId.Value();
} else {
rv = smsService->GetSmsDefaultServiceId(&serviceId);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return nullptr;
}
}
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
if (!global) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
if (!aSmscAddress.mAddress.WasPassed()) {
NS_WARNING("SmscAddress.address is a mandatory field and can not be omitted.");
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return promise.forget();
}
nsString address = aSmscAddress.mAddress.Value();
TypeOfNumber ton = aSmscAddress.mTypeOfAddress.mTypeOfNumber;
NumberPlanIdentification npi =
aSmscAddress.mTypeOfAddress.mNumberPlanIdentification;
// If the address begins with +, set TON to international no matter what has
// passed in.
if (!address.IsEmpty() && address[0] == '+') {
ton = TypeOfNumber::International;
}
nsCOMPtr<nsIMobileMessageCallback> msgCallback =
new MobileMessageCallback(promise);
rv = smsService->SetSmscAddress(serviceId, address,
static_cast<uint32_t>(ton), static_cast<uint32_t>(npi), msgCallback);
if (NS_FAILED(rv)) {
promise->MaybeReject(rv);
return promise.forget();
}
return promise.forget();
}
} // namespace dom
} // namespace mozilla
+7
View File
@@ -14,6 +14,7 @@
class nsISmsService;
class nsIDOMMozSmsMessage;
class nsIDOMMozMmsMessage;
class Promise;
namespace mozilla {
namespace dom {
@@ -25,6 +26,7 @@ struct MmsSendParameters;
struct MobileMessageFilter;
class OwningLongOrMozSmsMessageOrMozMmsMessage;
struct SmsSendParameters;
struct SmscAddress;
class MobileMessageManager final : public DOMEventTargetHelper
, public nsIObserver
@@ -116,6 +118,11 @@ public:
GetSmscAddress(const Optional<uint32_t>& aServiceId,
ErrorResult& aRv);
already_AddRefed<Promise>
SetSmscAddress(const SmscAddress& aSmscAddress,
const Optional<uint32_t>& aServiceId,
ErrorResult& aRv);
IMPL_EVENT_HANDLER(received)
IMPL_EVENT_HANDLER(retrieving)
IMPL_EVENT_HANDLER(sending)
+23 -4
View File
@@ -139,6 +139,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gpps",
"@mozilla.org/network/protocol-proxy-service;1",
"nsIProtocolProxyService");
XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
"@mozilla.org/icc/iccservice;1",
"nsIIccService");
XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
@@ -348,7 +352,7 @@ MmsConnection.prototype = {
// Get the proper IccInfo based on the current card type.
try {
let iccInfo = null;
let baseIccInfo = this.radioInterface.rilContext.iccInfo;
let baseIccInfo = this.getIccInfo();
if (baseIccInfo.iccType === 'ruim' || baseIccInfo.iccType === 'csim') {
iccInfo = baseIccInfo.QueryInterface(Ci.nsICdmaIccInfo);
number = iccInfo.mdn;
@@ -366,11 +370,27 @@ MmsConnection.prototype = {
return number;
},
/**
* A utility function to get IccInfo of the SIM card (if installed).
*/
getIccInfo: function() {
let icc = gIccService.getIccByServiceId(this.serviceId);
return icc ? icc.iccInfo : null;
},
/**
* A utility function to get CardState of the SIM card (if installed).
*/
getCardState: function() {
let icc = gIccService.getIccByServiceId(this.serviceId);
return icc ? icc.cardState : Ci.nsIIcc.CARD_STATE_UNKNOWN;
},
/**
* A utility function to get the ICC ID of the SIM card (if installed).
*/
getIccId: function() {
let iccInfo = this.radioInterface.rilContext.iccInfo;
let iccInfo = this.getIccInfo();
if (!iccInfo) {
return null;
@@ -405,8 +425,7 @@ MmsConnection.prototype = {
if (getRadioDisabledState()) {
if (DEBUG) debug("Error! Radio is disabled when sending MMS.");
errorStatus = _HTTP_STATUS_RADIO_DISABLED;
} else if (this.radioInterface.rilContext.cardState !=
Ci.nsIIcc.CARD_STATE_READY) {
} else if (this.getCardState() != Ci.nsIIcc.CARD_STATE_READY) {
if (DEBUG) debug("Error! SIM card is not ready when sending MMS.");
errorStatus = _HTTP_STATUS_NO_SIM_CARD;
}
+18 -5
View File
@@ -71,9 +71,13 @@ XPCOMUtils.defineLazyGetter(this, "gWAP", function() {
});
XPCOMUtils.defineLazyServiceGetter(this, "gCellBroadcastService",
"@mozilla.org/cellbroadcast/gonkservice;1",
"@mozilla.org/cellbroadcast/cellbroadcastservice;1",
"nsIGonkCellBroadcastService");
XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
"@mozilla.org/icc/iccservice;1",
"nsIIccService");
XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
"nsIMobileConnectionService");
@@ -150,7 +154,7 @@ SmsService.prototype = {
// Get the proper IccInfo based on the current card type.
try {
let iccInfo = null;
let baseIccInfo = gRadioInterfaces[aServiceId].rilContext.iccInfo;
let baseIccInfo = this._getIccInfo(aServiceId);
if (baseIccInfo.iccType === 'ruim' || baseIccInfo.iccType === 'csim') {
iccInfo = baseIccInfo.QueryInterface(Ci.nsICdmaIccInfo);
number = iccInfo.mdn;
@@ -168,8 +172,18 @@ SmsService.prototype = {
return number;
},
_getIccInfo: function(aServiceId) {
let icc = gIccService.getIccByServiceId(aServiceId);
return icc ? icc.iccInfo : null;
},
_getCardState: function(aServiceId) {
let icc = gIccService.getIccByServiceId(aServiceId);
return icc ? icc.cardState : Ci.nsIIcc.CARD_STATE_UNKNOWN;
},
_getIccId: function(aServiceId) {
let iccInfo = gRadioInterfaces[aServiceId].rilContext.iccInfo;
let iccInfo = this._getIccInfo(aServiceId);
if (!iccInfo) {
return null;
@@ -878,8 +892,7 @@ SmsService.prototype = {
radioState == Ci.nsIMobileConnection.MOBILE_RADIO_STATE_DISABLED) {
if (DEBUG) debug("Error! Radio is disabled when sending SMS.");
errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR;
} else if (gRadioInterfaces[aServiceId].rilContext.cardState !=
Ci.nsIIcc.CARD_STATE_READY) {
} else if (this._getCardState(aServiceId) != Ci.nsIIcc.CARD_STATE_READY) {
if (DEBUG) debug("Error! SIM card is not ready when sending SMS.");
errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR;
}
@@ -4,7 +4,7 @@
#include "nsISupports.idl"
[scriptable, uuid(35279dbc-9f1d-419f-b17a-230fcf49f0c7)]
[scriptable, uuid(b1367554-51c6-4153-b20a-effec50ca827)]
interface nsIMobileMessageCallback : nsISupports
{
/**
@@ -54,4 +54,6 @@ interface nsIMobileMessageCallback : nsISupports
*/
void notifyGetSmscAddress(in DOMString aSmscAddress);
void notifyGetSmscAddressFailed(in long error);
void notifySetSmscAddress();
void notifySetSmscAddressFailed(in long error);
};
+8 -3
View File
@@ -69,6 +69,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "messenger",
"@mozilla.org/system-message-internal;1",
"nsISystemMessagesInternal");
XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
"@mozilla.org/icc/iccservice;1",
"nsIIccService");
this.NetworkStatsService = {
init: function() {
debug("Service started");
@@ -253,11 +257,12 @@ this.NetworkStatsService = {
let networks = {};
let numRadioInterfaces = gRil.numRadioInterfaces;
for (let i = 0; i < numRadioInterfaces; i++) {
let icc = gIccService.getIccByServiceId(i);
let radioInterface = gRil.getRadioInterface(i);
if (radioInterface.rilContext.iccInfo) {
let netId = this.getNetworkId(radioInterface.rilContext.iccInfo.iccid,
if (icc && icc.iccInfo) {
let netId = this.getNetworkId(icc.iccInfo.iccid,
NET_TYPE_MOBILE);
networks[netId] = { id : radioInterface.rilContext.iccInfo.iccid,
networks[netId] = { id : icc.iccInfo.iccid,
type: NET_TYPE_MOBILE };
}
}
+274
View File
@@ -0,0 +1,274 @@
/* 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/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const DATACALLINTERFACE_CONTRACTID = "@mozilla.org/datacall/interface;1";
const DATACALLINTERFACESERVICE_CONTRACTID =
"@mozilla.org/datacall/interfaceservice;1";
const DATACALLINTERFACE_CID =
Components.ID("{ff669306-4390-462a-989b-ba37fc42153f}");
const DATACALLINTERFACESERVICE_CID =
Components.ID("{e23e9337-592d-40b9-8cef-7bd47c28b72e}");
const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
const TOPIC_PREF_CHANGED = "nsPref:changed";
const PREF_RIL_DEBUG_ENABLED = "ril.debugging.enabled";
XPCOMUtils.defineLazyGetter(this, "RIL", function () {
let obj = {};
Cu.import("resource://gre/modules/ril_consts.js", obj);
return obj;
});
XPCOMUtils.defineLazyServiceGetter(this, "gRil",
"@mozilla.org/ril;1",
"nsIRadioInterfaceLayer");
XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
"nsIMobileConnectionService");
var DEBUG = RIL.DEBUG_RIL;
function updateDebugFlag() {
// Read debug setting from pref
let debugPref;
try {
debugPref = Services.prefs.getBoolPref(PREF_RIL_DEBUG_ENABLED);
} catch (e) {
debugPref = false;
}
DEBUG = debugPref || RIL.DEBUG_RIL;
}
updateDebugFlag();
function DataCall(aAttributes) {
for (let key in aAttributes) {
if (key === "pdpType") {
// Convert pdp type into constant int value.
this[key] = RIL.RIL_DATACALL_PDP_TYPES.indexOf(aAttributes[key]);
continue;
}
this[key] = aAttributes[key];
}
}
DataCall.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCall]),
failCause: Ci.nsIDataCallInterface.DATACALL_FAIL_NONE,
suggestedRetryTime: -1,
cid: -1,
active: -1,
pdpType: -1,
ifname: null,
addreses: null,
dnses: null,
gateways: null
};
function DataCallInterfaceService() {
this._dataCallInterfaces = [];
let numClients = gRil.numRadioInterfaces;
for (let i = 0; i < numClients; i++) {
this._dataCallInterfaces.push(new DataCallInterface(i));
}
Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
Services.prefs.addObserver(PREF_RIL_DEBUG_ENABLED, this, false);
}
DataCallInterfaceService.prototype = {
classID: DATACALLINTERFACESERVICE_CID,
classInfo: XPCOMUtils.generateCI({
classID: DATACALLINTERFACESERVICE_CID,
contractID: DATACALLINTERFACESERVICE_CONTRACTID,
classDescription: "Data Call Interface Service",
interfaces: [Ci.nsIDataCallInterfaceService,
Ci.nsIGonkDataCallInterfaceService]
}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCallInterfaceService,
Ci.nsIGonkDataCallInterfaceService],
Ci.nsIObserver),
// An array of DataCallInterface instances.
_dataCallInterfaces: null,
debug: function(aMessage) {
dump("-*- DataCallInterfaceService: " + aMessage + "\n");
},
// nsIDataCallInterfaceService
getDataCallInterface: function(aClientId) {
let dataCallInterface = this._dataCallInterfaces[aClientId];
if (!dataCallInterface) {
throw Cr.NS_ERROR_UNEXPECTED;
}
return dataCallInterface;
},
// nsIGonkDataCallInterfaceService
notifyDataCallListChanged: function(aClientId, aCount, aDataCalls) {
let dataCallInterface = this.getDataCallInterface(aClientId);
dataCallInterface.handleDataCallListChanged(aCount, aDataCalls);
},
// nsIObserver
observe: function(aSubject, aTopic, aData) {
switch (aTopic) {
case TOPIC_PREF_CHANGED:
if (aData === PREF_RIL_DEBUG_ENABLED) {
updateDebugFlag();
}
break;
case TOPIC_XPCOM_SHUTDOWN:
Services.prefs.removeObserver(PREF_RIL_DEBUG_ENABLED, this);
Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
break;
}
},
};
function DataCallInterface(aClientId) {
this._clientId = aClientId;
this._radioInterface = gRil.getRadioInterface(aClientId);
this._listeners = [];
if (DEBUG) this.debug("DataCallInterface: " + aClientId);
}
DataCallInterface.prototype = {
classID: DATACALLINTERFACE_CID,
classInfo: XPCOMUtils.generateCI({classID: DATACALLINTERFACE_CID,
contractID: DATACALLINTERFACE_CONTRACTID,
classDescription: "Data Call Interface",
interfaces: [Ci.nsIDataCallInterface]}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCallInterface]),
debug: function(aMessage) {
dump("-*- DataCallInterface[" + this._clientId + "]: " + aMessage + "\n");
},
_clientId: -1,
_radioInterface: null,
_listeners: null,
// nsIDataCallInterface
setupDataCall: function(aApn, aUsername, aPassword, aAuthType, aPdpType,
aCallback) {
let connection =
gMobileConnectionService.getItemByServiceId(this._clientId);
let dataInfo = connection && connection.data;
let radioTechType = dataInfo.type;
let radioTechnology = RIL.GECKO_RADIO_TECH.indexOf(radioTechType);
// Convert pdp type into string value.
let pdpType = RIL.RIL_DATACALL_PDP_TYPES[aPdpType];
this._radioInterface.sendWorkerMessage("setupDataCall", {
radioTech: radioTechnology,
apn: aApn,
user: aUsername,
passwd: aPassword,
chappap: aAuthType,
pdptype: pdpType
}, (aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
} else {
let dataCall = new DataCall(aResponse);
aCallback.notifySetupDataCallSuccess(dataCall);
}
});
},
deactivateDataCall: function(aCid, aReason, aCallback) {
this._radioInterface.sendWorkerMessage("deactivateDataCall", {
cid: aCid,
reason: aReason
}, (aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
} else {
aCallback.notifySuccess();
}
});
},
getDataCallList: function(aCallback) {
this._radioInterface.sendWorkerMessage("getDataCallList", null,
(aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
} else {
let dataCalls = aResponse.datacalls.map(
dataCall => new DataCall(dataCall));
aCallback.notifyGetDataCallListSuccess(dataCalls.length, dataCalls);
}
});
},
setDataRegistration: function(aAttach, aCallback) {
this._radioInterface.sendWorkerMessage("setDataRegistration", {
attach: aAttach
}, (aResponse) => {
if (aResponse.errorMsg) {
aCallback.notifyError(aResponse.errorMsg);
} else {
aCallback.notifySuccess();
}
});
},
handleDataCallListChanged: function(aCount, aDataCalls) {
this._notifyAllListeners("notifyDataCallListChanged", [aCount, aDataCalls]);
},
_notifyAllListeners: function(aMethodName, aArgs) {
let listeners = this._listeners.slice();
for (let listener of listeners) {
if (this._listeners.indexOf(listener) == -1) {
// Listener has been unregistered in previous run.
continue;
}
let handler = listener[aMethodName];
try {
handler.apply(listener, aArgs);
} catch (e) {
if (DEBUG) {
this.debug("listener for " + aMethodName + " threw an exception: " + e);
}
}
}
},
registerListener: function(aListener) {
if (this._listeners.indexOf(aListener) >= 0) {
return;
}
this._listeners.push(aListener);
},
unregisterListener: function(aListener) {
let index = this._listeners.indexOf(aListener);
if (index >= 0) {
this._listeners.splice(index, 1);
}
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataCallInterfaceService]);
@@ -0,0 +1,6 @@
# 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/.
component {e23e9337-592d-40b9-8cef-7bd47c28b72e} DataCallInterfaceService.js
contract @mozilla.org/datacall/interfaceservice;1 {e23e9337-592d-40b9-8cef-7bd47c28b72e}
+25 -20
View File
@@ -43,6 +43,7 @@
#include "nsIMobileCellInfo.h"
#include "nsIMobileNetworkInfo.h"
#include "nsIRadioInterfaceLayer.h"
#include "nsIIccService.h"
#endif
#ifdef AGPS_TYPE_INVALID
@@ -476,31 +477,35 @@ GonkGPSGeolocationProvider::RequestSetID(uint32_t flags)
AGpsSetIDType type = AGPS_SETID_TYPE_NONE;
nsCOMPtr<nsIRilContext> rilCtx;
mRadioInterface->GetRilContext(getter_AddRefs(rilCtx));
nsCOMPtr<nsIIccService> iccService =
do_GetService(ICC_SERVICE_CONTRACTID);
NS_ENSURE_TRUE_VOID(iccService);
if (rilCtx) {
nsAutoString id;
if (flags & AGPS_RIL_REQUEST_SETID_IMSI) {
type = AGPS_SETID_TYPE_IMSI;
rilCtx->GetImsi(id);
}
nsCOMPtr<nsIIcc> icc;
iccService->GetIccByServiceId(mRilDataServiceId, getter_AddRefs(icc));
NS_ENSURE_TRUE_VOID(icc);
if (flags & AGPS_RIL_REQUEST_SETID_MSISDN) {
nsCOMPtr<nsIIccInfo> iccInfo;
rilCtx->GetIccInfo(getter_AddRefs(iccInfo));
if (iccInfo) {
nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
if (gsmIccInfo) {
type = AGPS_SETID_TYPE_MSISDN;
gsmIccInfo->GetMsisdn(id);
}
nsAutoString id;
if (flags & AGPS_RIL_REQUEST_SETID_IMSI) {
type = AGPS_SETID_TYPE_IMSI;
icc->GetImsi(id);
}
if (flags & AGPS_RIL_REQUEST_SETID_MSISDN) {
nsCOMPtr<nsIIccInfo> iccInfo;
icc->GetIccInfo(getter_AddRefs(iccInfo));
if (iccInfo) {
nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
if (gsmIccInfo) {
type = AGPS_SETID_TYPE_MSISDN;
gsmIccInfo->GetMsisdn(id);
}
}
NS_ConvertUTF16toUTF8 idBytes(id);
mAGpsRilInterface->set_set_id(type, idBytes.get());
}
NS_ConvertUTF16toUTF8 idBytes(id);
mAGpsRilInterface->set_set_id(type, idBytes.get());
}
void
+6 -431
View File
@@ -21,6 +21,7 @@ Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/* global RIL */
XPCOMUtils.defineLazyGetter(this, "RIL", function () {
let obj = {};
Cu.import("resource://gre/modules/ril_consts.js", obj);
@@ -43,11 +44,6 @@ const RILCONTENTHELPER_CID =
Components.ID("{472816e1-1fd6-4405-996c-806f9ea68174}");
const RIL_IPC_MSG_NAMES = [
"RIL:CardStateChanged",
"RIL:IccInfoChanged",
"RIL:GetCardLockResult",
"RIL:SetUnlockCardLockResult",
"RIL:CardLockRetryCount",
"RIL:StkCommand",
"RIL:StkSessionEnd",
"RIL:IccOpenChannel",
@@ -55,18 +51,19 @@ const RIL_IPC_MSG_NAMES = [
"RIL:IccExchangeAPDU",
"RIL:ReadIccContacts",
"RIL:UpdateIccContact",
"RIL:MatchMvno",
"RIL:GetServiceState"
];
/* global cpmm */
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsISyncMessageSender");
/* global UUIDGenerator */
XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
/* global gNumRadioInterfaces */
XPCOMUtils.defineLazyGetter(this, "gNumRadioInterfaces", function() {
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
let isParentProcess = !appInfo || appInfo.getService(Ci.nsIXULRuntime)
@@ -77,69 +74,20 @@ XPCOMUtils.defineLazyGetter(this, "gNumRadioInterfaces", function() {
try {
ril = Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
} catch(e) {}
return ril.numRadioInterfaces
return ril.numRadioInterfaces;
}
return Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
});
function IccInfo() {}
IccInfo.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
// nsIIccInfo
iccType: null,
iccid: null,
mcc: null,
mnc: null,
spn: null,
isDisplayNetworkNameRequired: false,
isDisplaySpnRequired: false
};
function GsmIccInfo() {}
GsmIccInfo.prototype = {
__proto__: IccInfo.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
Ci.nsIIccInfo]),
// nsIGsmIccInfo
msisdn: null
};
function CdmaIccInfo() {}
CdmaIccInfo.prototype = {
__proto__: IccInfo.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
Ci.nsIIccInfo]),
// nsICdmaIccInfo
mdn: null,
prlVersion: 0
};
function RILContentHelper() {
this.updateDebugFlag();
this.numClients = gNumRadioInterfaces;
if (DEBUG) debug("Number of clients: " + this.numClients);
this._iccs = [];
this.rilContexts = [];
for (let clientId = 0; clientId < this.numClients; clientId++) {
this._iccs.push(new Icc(this, clientId));
this.rilContexts[clientId] = {
cardState: Ci.nsIIcc.CARD_STATE_UNKNOWN,
iccInfo: null
};
}
this.initDOMRequestHelper(/* aWindow */ null, RIL_IPC_MSG_NAMES);
this._windowsMap = [];
this._requestMap = [];
this._iccListeners = [];
this._iccChannelCallback = [];
@@ -152,14 +100,12 @@ RILContentHelper.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccProvider,
Ci.nsIIccService,
Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
classID: RILCONTENTHELPER_CID,
classInfo: XPCOMUtils.generateCI({classID: RILCONTENTHELPER_CID,
classDescription: "RILContentHelper",
interfaces: [Ci.nsIIccProvider,
Ci.nsIIccService]}),
interfaces: [Ci.nsIIccProvider]}),
updateDebugFlag: function() {
try {
@@ -168,71 +114,8 @@ RILContentHelper.prototype = {
} catch (e) {}
},
// An utility function to copy objects.
updateInfo: function(srcInfo, destInfo) {
for (let key in srcInfo) {
destInfo[key] = srcInfo[key];
}
},
/**
* We need to consider below cases when update iccInfo:
* 1. Should clear iccInfo to null if there is no card detected.
* 2. Need to create corresponding object based on iccType.
*/
updateIccInfo: function(clientId, newInfo) {
let rilContext = this.rilContexts[clientId];
// Card is not detected, clear iccInfo to null.
if (!newInfo || !newInfo.iccid) {
if (rilContext.iccInfo) {
rilContext.iccInfo = null;
}
return;
}
// If iccInfo is null, new corresponding object based on iccType.
if (!rilContext.iccInfo) {
if (newInfo.iccType === "ruim" || newInfo.iccType === "csim") {
rilContext.iccInfo = new CdmaIccInfo();
} else if (newInfo.iccType === "sim" || newInfo.iccType === "usim") {
rilContext.iccInfo = new GsmIccInfo();
} else {
rilContext.iccInfo = new IccInfo();
}
}
this.updateInfo(newInfo, rilContext.iccInfo);
},
_windowsMap: null,
_requestMap: null,
rilContexts: null,
getRilContext: function(clientId) {
// Update ril contexts by sending IPC message to chrome only when the first
// time we require it. The information will be updated by following info
// changed messages.
this.getRilContext = function getRilContext(clientId) {
return this.rilContexts[clientId];
};
for (let cId = 0; cId < this.numClients; cId++) {
let rilContext =
cpmm.sendSyncMessage("RIL:GetRilContext", {clientId: cId})[0];
if (!rilContext) {
if (DEBUG) debug("Received null rilContext from chrome process.");
continue;
}
this.rilContexts[cId].cardState = rilContext.cardState;
this.updateIccInfo(cId, rilContext.iccInfo);
}
return this.rilContexts[clientId];
},
/**
* nsIIccProvider
*/
@@ -564,61 +447,6 @@ RILContentHelper.prototype = {
let data = msg.json.data;
let clientId = msg.json.clientId;
switch (msg.name) {
case "RIL:CardStateChanged":
if (this.rilContexts[clientId].cardState != data.cardState) {
this.rilContexts[clientId].cardState = data.cardState;
this._deliverIccEvent(clientId,
"notifyCardStateChanged",
null);
}
break;
case "RIL:IccInfoChanged":
this.updateIccInfo(clientId, data);
this._deliverIccEvent(clientId,
"notifyIccInfoChanged",
null);
break;
case "RIL:GetCardLockResult": {
let requestId = data.requestId;
let callback = this._requestMap[requestId];
delete this._requestMap[requestId];
if (data.errorMsg) {
callback.notifyError(data.errorMsg);
break;
}
callback.notifySuccessWithBoolean(data.enabled);
break;
}
case "RIL:SetUnlockCardLockResult": {
let requestId = data.requestId;
let callback = this._requestMap[requestId];
delete this._requestMap[requestId];
if (data.errorMsg) {
let retryCount =
(data.retryCount !== undefined) ? data.retryCount : -1;
callback.notifyCardLockError(data.errorMsg, retryCount);
break;
}
callback.notifySuccess();
break;
}
case "RIL:CardLockRetryCount": {
let requestId = data.requestId;
let callback = this._requestMap[requestId];
delete this._requestMap[requestId];
if (data.errorMsg) {
callback.notifyError(data.errorMsg);
break;
}
callback.notifyGetCardLockRetryCount(data.retryCount);
break;
}
case "RIL:StkCommand":
this._deliverEvent(clientId, "_iccListeners", "notifyStkCommand",
[JSON.stringify(data)]);
@@ -641,30 +469,6 @@ RILContentHelper.prototype = {
case "RIL:UpdateIccContact":
this.handleUpdateIccContact(data);
break;
case "RIL:MatchMvno": {
let requestId = data.requestId;
let callback = this._requestMap[requestId];
delete this._requestMap[requestId];
if (data.errorMsg) {
callback.notifyError(data.errorMsg);
break;
}
callback.notifySuccessWithBoolean(data.result);
break;
}
case "RIL:GetServiceState": {
let requestId = data.requestId;
let callback = this._requestMap[requestId];
delete this._requestMap[requestId];
if (data.errorMsg) {
callback.notifyError(data.errorMsg);
break;
}
callback.notifySuccessWithBoolean(data.result);
break;
}
}
},
@@ -791,235 +595,6 @@ RILContentHelper.prototype = {
if (DEBUG) debug("listener for " + name + " threw an exception: " + e);
}
}
},
/**
* nsIIccService interface.
*/
_iccs: null, // An array of Icc instances.
getIccByServiceId: function(serviceId) {
let icc = this._iccs[serviceId];
if (!icc) {
throw Cr.NS_ERROR_UNEXPECTED;
}
return icc;
},
/**
* Bridge APIs from nsIIccService to nsIIccProvider
*/
_deliverIccEvent: function(clientId, name, args) {
let icc = this._iccs[clientId];
if (!icc) {
if (DEBUG) debug("_deliverIccEvent: Invalid clientId: " + clientId);
return;
}
icc.deliverListenerEvent(name, args);
},
getIccInfo: function(clientId) {
let context = this.getRilContext(clientId);
return context && context.iccInfo;
},
getCardState: function(clientId) {
let context = this.getRilContext(clientId);
return context && context.cardState;
},
matchMvno: function(clientId, mvnoType, mvnoData, callback) {
let requestId = UUIDGenerator.generateUUID().toString();
this._requestMap[requestId] = callback;
cpmm.sendAsyncMessage("RIL:MatchMvno", {
clientId: clientId,
data: {
requestId: requestId,
mvnoType: mvnoType,
mvnoData: mvnoData
}
});
},
getCardLockEnabled: function(clientId, lockType, callback) {
let requestId = UUIDGenerator.generateUUID().toString();
this._requestMap[requestId] = callback;
cpmm.sendAsyncMessage("RIL:GetCardLockEnabled", {
clientId: clientId,
data: {
lockType: lockType,
requestId: requestId
}
});
},
unlockCardLock: function(clientId, lockType, password, newPin, callback) {
let requestId = UUIDGenerator.generateUUID().toString();
this._requestMap[requestId] = callback;
cpmm.sendAsyncMessage("RIL:UnlockCardLock", {
clientId: clientId,
data: {
lockType: lockType,
password: password,
newPin: newPin,
requestId: requestId
}
});
},
setCardLockEnabled: function(clientId, lockType, password, enabled, callback) {
let requestId = UUIDGenerator.generateUUID().toString();
this._requestMap[requestId] = callback;
cpmm.sendAsyncMessage("RIL:SetCardLockEnabled", {
clientId: clientId,
data: {
lockType: lockType,
password: password,
enabled: enabled,
requestId: requestId
}
});
},
changeCardLockPassword: function(clientId, lockType, password, newPassword,
callback) {
let requestId = UUIDGenerator.generateUUID().toString();
this._requestMap[requestId] = callback;
cpmm.sendAsyncMessage("RIL:ChangeCardLockPassword", {
clientId: clientId,
data: {
lockType: lockType,
password: password,
newPassword: newPassword,
requestId: requestId
}
});
},
getCardLockRetryCount: function(clientId, lockType, callback) {
let requestId = UUIDGenerator.generateUUID().toString();
this._requestMap[requestId] = callback;
cpmm.sendAsyncMessage("RIL:GetCardLockRetryCount", {
clientId: clientId,
data: {
lockType: lockType,
requestId: requestId
}
});
},
getServiceStateEnabled: function(clientId, service, callback) {
let requestId = UUIDGenerator.generateUUID().toString();
this._requestMap[requestId] = callback;
cpmm.sendAsyncMessage("RIL:GetServiceState", {
clientId: clientId,
data: {
requestId: requestId,
service: service
}
});
}
};
function Icc(aIccProvider, aClientId) {
this._iccProvider = aIccProvider;
this._clientId = aClientId;
this._listeners = [];
}
Icc.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIIcc]),
_iccProvider: null,
_clientId: -1,
_listeners: null,
deliverListenerEvent: function(aName, aArgs) {
let listeners = this._listeners.slice();
for (let listener of listeners) {
if (this._listeners.indexOf(listener) === -1) {
continue;
}
let handler = listener[aName];
if (typeof handler != "function") {
throw new Error("No handler for " + aName);
}
try {
handler.apply(listener, aArgs);
} catch (e) {
if (DEBUG) {
debug("listener for " + aName + " threw an exception: " + e);
}
}
}
},
/**
* nsIIcc interface.
*/
registerListener: function(aListener) {
if (this._listeners.indexOf(aListener) >= 0) {
throw Cr.NS_ERROR_UNEXPECTED;
}
this._listeners.push(aListener);
cpmm.sendAsyncMessage("RIL:RegisterIccMsg");
},
unregisterListener: function(aListener) {
let index = this._listeners.indexOf(aListener);
if (index >= 0) {
this._listeners.splice(index, 1);
}
},
get iccInfo() {
return this._iccProvider.getIccInfo(this._clientId);
},
get cardState() {
return this._iccProvider.getCardState(this._clientId);
},
getCardLockEnabled: function(aLockType, aCallback) {
this._iccProvider.getCardLockEnabled(this._clientId, aLockType, aCallback);
},
unlockCardLock: function(aLockType, aPassword, aNewPin, aCallback) {
this._iccProvider.unlockCardLock(this._clientId, aLockType,
aPassword, aNewPin, aCallback);
},
setCardLockEnabled: function(aLockType, aPassword, aEnabled, aCallback) {
this._iccProvider.setCardLockEnabled(this._clientId, aLockType,
aPassword, aEnabled, aCallback);
},
changeCardLockPassword: function(aLockType, aPassword, aNewPassword, aCallback) {
this._iccProvider.changeCardLockPassword(this._clientId, aLockType,
aPassword, aNewPassword, aCallback);
},
getCardLockRetryCount: function(aLockType, aCallback) {
this._iccProvider.getCardLockRetryCount(this._clientId, aLockType, aCallback);
},
matchMvno: function(aMvnoType, aMvnoData, aCallback) {
this._iccProvider.matchMvno(this._clientId, aMvnoType, aMvnoData, aCallback);
},
getServiceStateEnabled: function(aService, aCallback) {
this._iccProvider.getServiceStateEnabled(this._clientId, aService, aCallback);
}
};
+259 -429
View File
@@ -47,6 +47,9 @@ let RILQUIRKS_RADIO_OFF_WO_CARD =
let RILQUIRKS_HAVE_IPV6 =
libcutils.property_get("ro.moz.ril.ipv6", "false") == "true";
let RILQUIRKS_SIGNAL_EXTRA_INT32 =
libcutils.property_get("ro.moz.ril.signal_extra_int", "false") == "true";
const RADIOINTERFACELAYER_CID =
Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
const RADIOINTERFACE_CID =
@@ -86,24 +89,16 @@ const NETWORK_TYPE_MOBILE_DUN = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN;
// TODO: Bug 815526, deprecate RILContentHelper.
const RIL_IPC_ICCMANAGER_MSG_NAMES = [
"RIL:GetRilContext",
"RIL:SendStkResponse",
"RIL:SendStkMenuSelection",
"RIL:SendStkTimerExpiration",
"RIL:SendStkEventDownload",
"RIL:GetCardLockEnabled",
"RIL:UnlockCardLock",
"RIL:SetCardLockEnabled",
"RIL:ChangeCardLockPassword",
"RIL:GetCardLockRetryCount",
"RIL:IccOpenChannel",
"RIL:IccExchangeAPDU",
"RIL:IccCloseChannel",
"RIL:ReadIccContacts",
"RIL:UpdateIccContact",
"RIL:RegisterIccMsg",
"RIL:MatchMvno",
"RIL:GetServiceState"
];
// set to true in ril_consts.js to see debug messages
@@ -166,7 +161,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
"nsIGonkMobileConnectionService");
XPCOMUtils.defineLazyServiceGetter(this, "gCellBroadcastService",
"@mozilla.org/cellbroadcast/gonkservice;1",
"@mozilla.org/cellbroadcast/cellbroadcastservice;1",
"nsIGonkCellBroadcastService");
XPCOMUtils.defineLazyServiceGetter(this, "gIccMessenger",
@@ -878,44 +873,6 @@ try {
})());
} catch (e) {}
function IccInfo() {}
IccInfo.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
// nsIIccInfo
iccType: null,
iccid: null,
mcc: null,
mnc: null,
spn: null,
isDisplayNetworkNameRequired: false,
isDisplaySpnRequired: false
};
function GsmIccInfo() {}
GsmIccInfo.prototype = {
__proto__: IccInfo.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
Ci.nsIIccInfo]),
// nsIGsmIccInfo
msisdn: null
};
function CdmaIccInfo() {}
CdmaIccInfo.prototype = {
__proto__: IccInfo.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
Ci.nsIIccInfo]),
// nsICdmaIccInfo
mdn: null,
prlVersion: 0
};
function DataConnectionHandler(clientId, radioInterface) {
// Initial owning attributes.
this.clientId = clientId;
@@ -987,42 +944,13 @@ DataConnectionHandler.prototype = {
},
_compareDataCallOptions: function(dataCall, newDataCall) {
return dataCall.apnProfile.apn == newDataCall.apn &&
dataCall.apnProfile.user == newDataCall.user &&
dataCall.apnProfile.password == newDataCall.passwd &&
return dataCall.apnProfile.apn == newDataCall.apnProfile.apn &&
dataCall.apnProfile.user == newDataCall.apnProfile.user &&
dataCall.apnProfile.password == newDataCall.apnProfile.passwd &&
dataCall.chappap == newDataCall.chappap &&
dataCall.pdptype == newDataCall.pdptype;
},
_deliverDataCallMessage: function(name, args) {
for (let i = 0; i < this._dataCalls.length; i++) {
let datacall = this._dataCalls[i];
// Send message only to the DataCall that matches the data call options.
// Currently, args always contain only one datacall info.
if (!this._compareDataCallOptions(datacall, args[0])) {
continue;
}
// Do not deliver message to DataCall that contains cid but mistmaches
// with the cid in the current message.
if (args[0].cid !== undefined && datacall.linkInfo.cid != null &&
args[0].cid != datacall.linkInfo.cid) {
continue;
}
try {
let handler = datacall[name];
if (typeof handler !== "function") {
throw new Error("No handler for " + name);
}
handler.apply(datacall, args);
} catch (e) {
if (DEBUG) {
this.debug("Handler for " + name + " threw an exception: " + e);
}
}
}
},
/**
* This function will do the following steps:
* 1. Clear the cached APN settings in the RIL.
@@ -1334,10 +1262,62 @@ DataConnectionHandler.prototype = {
return dataDisconnecting;
},
_findDataCallByCid: function(cid) {
if (cid === undefined || cid < 0) {
return -1;
}
for (let i = 0; i < this._dataCalls.length; i++) {
let datacall = this._dataCalls[i];
if (datacall.linkInfo.cid != null &&
datacall.linkInfo.cid === cid) {
return i;
}
}
return -1;
},
/**
* Handle data errors.
* Handle unsolicidated data call list changed, called from RadioInterface.
*/
handleDataCallError: function(message) {
handleDataCallListChanged: function(dataCallList) {
let currentDataCalls = this._dataCalls.slice();
for (let i = 0; i < dataCallList.length; i++) {
let dataCall = dataCallList[i];
let index = this._findDataCallByCid(dataCall.cid);
if (index == -1) {
if (DEBUG) {
this.debug("Unexpected new data call: " + JSON.stringify(dataCall));
}
continue;
}
currentDataCalls[index].onDataCallChanged(dataCall);
currentDataCalls[index] = null;
}
// If there is any CONNECTED DataCall left in currentDataCalls, means that
// it is missing in dataCallList, we should send a DISCONNECTED event to
// notify about this.
for (let i = 0; i < currentDataCalls.length; i++) {
let currentDataCall = currentDataCalls[i];
if (currentDataCall && currentDataCall.linkInfo.cid != null &&
currentDataCall.state == RIL.GECKO_NETWORK_STATE_CONNECTED) {
if (DEBUG) {
this.debug("Expected data call missing: " + JSON.stringify(
currentDataCall.apnProfile) + ", must have been DISCONNECTED.");
}
currentDataCall.onDataCallChanged({
state: RIL.GECKO_NETWORK_STATE_DISCONNECTED
});
}
}
},
/**
* Notify about data call setup error, called from DataCall.
*/
notifyDataCallError: function(message) {
// Notify data call error only for data APN
let networkInterface = this.dataNetworkInterfaces.get(NETWORK_TYPE_MOBILE);
if (networkInterface && networkInterface.enabled) {
@@ -1345,7 +1325,7 @@ DataConnectionHandler.prototype = {
// If there is a cid, compare cid; otherwise it is probably an error on
// data call setup.
if (message.cid !== undefined) {
if (message.cid == dataCall.linkInfo.cid) {
if (message.linkInfo.cid == dataCall.linkInfo.cid) {
gMobileConnectionService.notifyDataError(this.clientId, message);
}
} else {
@@ -1354,23 +1334,20 @@ DataConnectionHandler.prototype = {
}
}
}
this._deliverDataCallMessage("dataCallError", [message]);
},
/**
* Handle data call state changes.
* Notify about data call changed, called from DataCall.
*/
handleDataCallState: function(datacall) {
this._deliverDataCallMessage("dataCallStateChanged", [datacall]);
notifyDataCallChanged: function(updatedDataCall) {
// Process pending radio power off request after all data calls
// are disconnected.
if (datacall.state == RIL.GECKO_NETWORK_STATE_DISCONNECTED &&
if (updatedDataCall.state == RIL.GECKO_NETWORK_STATE_DISCONNECTED ||
updatedDataCall.state == RIL.GECKO_NETWORK_STATE_UNKNOWN &&
this.allDataDisconnected()) {
if (gRadioEnabledController.isDeactivatingDataCalls()) {
if (DEBUG) {
this.debug("All data connections are disconnected.");
this.debug("All data calls are disconnected.");
}
gRadioEnabledController.finishDeactivatingDataCalls(this.clientId);
}
@@ -1418,7 +1395,6 @@ RadioInterfaceLayer.prototype = {
interfaces: [Ci.nsIRadioInterfaceLayer]}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIRadioInterfaceLayer,
Ci.nsIRadioInterfaceLayer_new, // TODO: Bug 815526, deprecate RILContentHelper.
Ci.nsIObserver]),
/**
@@ -1519,7 +1495,8 @@ WorkerMessenger.prototype = {
sendStkProfileDownload:
libcutils.property_get("ro.moz.ril.send_stk_profile_dl", "false") == "true",
dataRegistrationOnDemand: RILQUIRKS_DATA_REGISTRATION_ON_DEMAND,
subscriptionControl: RILQUIRKS_SUBSCRIPTION_CONTROL
subscriptionControl: RILQUIRKS_SUBSCRIPTION_CONTROL,
signalExtraInt: RILQUIRKS_SIGNAL_EXTRA_INT32
}
};
@@ -1665,12 +1642,6 @@ function RadioInterface(aClientId, aWorkerMessenger) {
};
aWorkerMessenger.registerClient(aClientId, this);
this.rilContext = {
cardState: Ci.nsIIcc.CARD_STATE_UNKNOWN,
iccInfo: null,
imsi: null
};
this.operatorInfo = {};
let lock = gSettingsService.createLock();
@@ -1728,42 +1699,14 @@ RadioInterface.prototype = {
Services.obs.removeObserver(this, kNetworkConnStateChangedTopic);
},
/**
* A utility function to copy objects. The srcInfo may contain
* "rilMessageType", should ignore it.
*/
updateInfo: function(srcInfo, destInfo) {
for (let key in srcInfo) {
if (key === "rilMessageType") {
continue;
}
destInfo[key] = srcInfo[key];
}
},
/**
* A utility function to compare objects. The srcInfo may contain
* "rilMessageType", should ignore it.
*/
isInfoChanged: function(srcInfo, destInfo) {
if (!destInfo) {
return true;
}
for (let key in srcInfo) {
if (key === "rilMessageType") {
continue;
}
if (srcInfo[key] !== destInfo[key]) {
return true;
}
}
return false;
getIccInfo: function() {
let icc = gIccService.getIccByServiceId(this.clientId);
return icc ? icc.iccInfo : null;
},
isCardPresent: function() {
let cardState = this.rilContext.cardState;
let icc = gIccService.getIccByServiceId(this.clientId);
let cardState = icc ? icc.cardState : Ci.nsIIcc.CARD_STATE_UNKNOWN;
return cardState !== Ci.nsIIcc.CARD_STATE_UNDETECTED &&
cardState !== Ci.nsIIcc.CARD_STATE_UNKNOWN;
},
@@ -1775,29 +1718,6 @@ RadioInterface.prototype = {
*/
receiveMessage: function(msg) {
switch (msg.name) {
case "RIL:GetRilContext":
// This message is sync.
return this.rilContext;
case "RIL:GetCardLockEnabled":
this.workerMessenger.sendWithIPCMessage(msg, "iccGetCardLockEnabled",
"RIL:GetCardLockResult");
break;
case "RIL:UnlockCardLock":
this.workerMessenger.sendWithIPCMessage(msg, "iccUnlockCardLock",
"RIL:SetUnlockCardLockResult");
break;
case "RIL:SetCardLockEnabled":
this.workerMessenger.sendWithIPCMessage(msg, "iccSetCardLockEnabled",
"RIL:SetUnlockCardLockResult");
break;
case "RIL:ChangeCardLockPassword":
this.workerMessenger.sendWithIPCMessage(msg, "iccChangeCardLockPassword",
"RIL:SetUnlockCardLockResult");
break;
case "RIL:GetCardLockRetryCount":
this.workerMessenger.sendWithIPCMessage(msg, "iccGetCardLockRetryCount",
"RIL:CardLockRetryCount");
break;
case "RIL:SendStkResponse":
this.workerMessenger.send("sendStkTerminalResponse", msg.json.data);
break;
@@ -1825,12 +1745,6 @@ RadioInterface.prototype = {
case "RIL:UpdateIccContact":
this.workerMessenger.sendWithIPCMessage(msg, "updateICCContact");
break;
case "RIL:MatchMvno":
this.matchMvno(msg.target, msg.json.data);
break;
case "RIL:GetServiceState":
this.workerMessenger.sendWithIPCMessage(msg, "getIccServiceState");
break;
}
return null;
},
@@ -1838,20 +1752,11 @@ RadioInterface.prototype = {
handleUnsolicitedWorkerMessage: function(message) {
let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
switch (message.rilMessageType) {
case "audioStateChanged":
gTelephonyService.notifyAudioStateChanged(this.clientId, message.state);
break;
case "callRing":
gTelephonyService.notifyCallRing();
break;
case "callStateChange":
gTelephonyService.notifyCallStateChanged(this.clientId, message.call);
break;
case "callDisconnected":
gTelephonyService.notifyCallDisconnected(this.clientId, message.call);
break;
case "conferenceCallStateChanged":
gTelephonyService.notifyConferenceCallStateChanged(message.state);
case "currentCalls":
gTelephonyService.notifyCurrentCalls(this.clientId, message.calls);
break;
case "cdmaCallWaiting":
gTelephonyService.notifyCdmaCallWaiting(this.clientId,
@@ -1859,29 +1764,15 @@ RadioInterface.prototype = {
break;
case "suppSvcNotification":
gTelephonyService.notifySupplementaryService(this.clientId,
message.callIndex,
message.number,
message.notification);
break;
case "ussdreceived":
gTelephonyService.notifyUssdReceived(this.clientId, message.message,
message.sessionEnded);
break;
case "datacallerror":
connHandler.handleDataCallError(message);
break;
case "datacallstatechange":
let addresses = [];
for (let i = 0; i < message.addresses.length; i++) {
let [address, prefixLength] = message.addresses[i].split("/");
// From AOSP hardware/ril/include/telephony/ril.h, that address prefix
// is said to be OPTIONAL, but we never met such case before.
addresses.push({
address: address,
prefixLength: prefixLength ? parseInt(prefixLength, 10) : 0
});
}
message.addresses = addresses;
connHandler.handleDataCallState(message);
case "datacalllistchanged":
connHandler.handleDataCallListChanged(message.datacalls);
break;
case "emergencyCbModeChange":
gMobileConnectionService.notifyEmergencyCallbackModeChanged(this.clientId,
@@ -1923,13 +1814,9 @@ RadioInterface.prototype = {
message.radioState);
break;
case "cardstatechange":
this.rilContext.cardState = message.cardState;
gRadioEnabledController.receiveCardState(this.clientId);
gIccService.notifyCardStateChanged(this.clientId,
this.rilContext.cardState);
// TODO: Bug 815526, deprecate RILContentHelper.
gMessageManager.sendIccMessage("RIL:CardStateChanged",
this.clientId, message);
message.cardState);
gRadioEnabledController.receiveCardState(this.clientId);
break;
case "sms-received":
this.handleSmsReceived(message);
@@ -1941,11 +1828,11 @@ RadioInterface.prototype = {
this.handleNitzTime(message);
break;
case "iccinfochange":
this.handleIccInfoChange(message);
gIccService.notifyIccInfoChanged(this.clientId,
message.iccid ? message : null);
break;
case "iccimsi":
this.rilContext.imsi = message.imsi;
gIccService.notifyImsiChanged(this.clientId, this.rilContext.imsi);
gIccService.notifyImsiChanged(this.clientId, message.imsi);
break;
case "iccmbdn":
this.handleIccMbdn(message);
@@ -1969,85 +1856,6 @@ RadioInterface.prototype = {
}
},
// Matches the mvnoData pattern with imsi. Characters 'x' and 'X' are skipped
// and not compared. E.g., if the mvnoData passed is '310260x10xxxxxx',
// then the function returns true only if imsi has the same first 6 digits,
// 8th and 9th digit.
// TODO: Bug 815526, deprecate RILContentHelper.
isImsiMatches: function(mvnoData) {
let imsi = this.rilContext.imsi;
// This should not be an error, but a mismatch.
if (mvnoData.length > imsi.length) {
return false;
}
for (let i = 0; i < mvnoData.length; i++) {
let c = mvnoData[i];
if ((c !== 'x') && (c !== 'X') && (c !== imsi[i])) {
return false;
}
}
return true;
},
// TODO: Bug 815526, deprecate RILContentHelper.
matchMvno: function(target, message) {
if (DEBUG) this.debug("matchMvno: " + JSON.stringify(message));
if (!message || !message.mvnoData) {
message.errorMsg = RIL.GECKO_ERROR_INVALID_PARAMETER;
}
if (!message.errorMsg) {
switch (message.mvnoType) {
case RIL.GECKO_CARDMVNO_TYPE_IMSI:
if (!this.rilContext.imsi) {
message.errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE;
break;
}
message.result = this.isImsiMatches(message.mvnoData);
break;
case RIL.GECKO_CARDMVNO_TYPE_SPN:
let spn = this.rilContext.iccInfo && this.rilContext.iccInfo.spn;
if (!spn) {
message.errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE;
break;
}
message.result = spn == message.mvnoData;
break;
case RIL.GECKO_CARDMVNO_TYPE_GID:
this.workerMessenger.send("getGID1", null, (function(response) {
let gid = response.gid1;
let mvnoDataLength = message.mvnoData.length;
if (!gid) {
message.errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE;
} else if (mvnoDataLength > gid.length) {
message.result = false;
} else {
message.result =
gid.substring(0, mvnoDataLength).toLowerCase() ==
message.mvnoData.toLowerCase();
}
target.sendAsyncMessage("RIL:MatchMvno", {
clientId: this.clientId,
data: message
});
}).bind(this));
return;
default:
message.errorMsg = RIL.GECKO_ERROR_MODE_NOT_SUPPORTED;
}
}
target.sendAsyncMessage("RIL:MatchMvno", {
clientId: this.clientId,
data: message
});
},
setDataRegistration: function(attach) {
let deferred = Promise.defer();
this.workerMessenger.send("setDataRegistration",
@@ -2226,71 +2034,12 @@ RadioInterface.prototype = {
null, null);
},
handleIccInfoChange: function(message) {
let oldSpn = this.rilContext.iccInfo ? this.rilContext.iccInfo.spn : null;
// TODO: Bug 815526, deprecate RILContentHelper:
// Move the logic of updating iccInfo to IccService.js.
if (!message || !message.iccid) {
// If iccInfo is already `null`, don't have to clear it and send
// RIL:IccInfoChanged.
if (!this.rilContext.iccInfo) {
return;
}
// Card is not detected, clear iccInfo to null.
this.rilContext.iccInfo = null;
} else {
if (!this.rilContext.iccInfo) {
if (message.iccType === "ruim" || message.iccType === "csim") {
this.rilContext.iccInfo = new CdmaIccInfo();
} else if (message.iccType === "sim" || message.iccType === "usim") {
this.rilContext.iccInfo = new GsmIccInfo();
} else {
this.rilContext.iccInfo = new IccInfo();
}
}
if (!this.isInfoChanged(message, this.rilContext.iccInfo)) {
return;
}
this.updateInfo(message, this.rilContext.iccInfo);
}
// RIL:IccInfoChanged corresponds to a DOM event that gets fired only
// when iccInfo has changed.
// TODO: Bug 815526, deprecate RILContentHelper.
gMessageManager.sendIccMessage("RIL:IccInfoChanged",
this.clientId,
message.iccid ? message : null);
gIccService.notifyIccInfoChanged(this.clientId,
message.iccid ? message : null);
// Update lastKnownHomeNetwork.
if (message.mcc && message.mnc) {
let lastKnownHomeNetwork = message.mcc + "-" + message.mnc;
// Append spn information if available.
if (message.spn) {
lastKnownHomeNetwork += "-" + message.spn;
}
gMobileConnectionService.notifyLastHomeNetworkChanged(this.clientId,
lastKnownHomeNetwork);
}
// If spn becomes available, we should check roaming again.
if (!oldSpn && message.spn) {
gMobileConnectionService.notifySpnAvailable(this.clientId);
}
},
handleStkProactiveCommand: function(message) {
if (DEBUG) this.debug("handleStkProactiveCommand " + JSON.stringify(message));
let iccId = this.rilContext.iccInfo && this.rilContext.iccInfo.iccid;
if (iccId) {
let iccInfo = this.getIccInfo();
if (iccInfo && iccInfo.iccid) {
gIccMessenger
.notifyStkProactiveCommand(iccId,
.notifyStkProactiveCommand(iccInfo.iccid,
gStkCmdFactory.createCommand(message));
}
// TODO: Bug 815526, deprecate RILContentHelper.
@@ -2588,8 +2337,6 @@ RadioInterface.prototype = {
// nsIRadioInterface
rilContext: null,
// TODO: Bug 928861 - B2G NetworkManager: Provide a more generic function
// for connecting
setupDataCallByType: function(networkType) {
@@ -2642,8 +2389,7 @@ function DataCall(clientId, apnSetting) {
this.linkInfo = {
cid: null,
ifname: null,
ips: [],
prefixLengths: [],
addresses: [],
dnses: [],
gateways: []
};
@@ -2675,98 +2421,169 @@ DataCall.prototype = {
// Holds the authentication type sent to ril worker.
chappap: null,
dataCallError: function(message) {
if (DEBUG) {
this.debug("Data call error on APN " + message.apn + ": " +
message.errorMsg + " (" + message.status + "), retry time: " +
message.suggestedRetryTime);
}
this.state = RIL.GECKO_NETWORK_STATE_DISCONNECTED;
if (this.requestedNetworkIfaces.length === 0) {
if (DEBUG) this.debug("This DataCall is not requested anymore.");
return;
/**
* @return "deactivate" if <ifname> changes or one of the currentDataCall
* addresses is missing in updatedDataCall, or "identical" if no
* changes found, or "changed" otherwise.
*/
_compareDataCallLink: function(updatedDataCall, currentDataCall) {
// If network interface is changed, report as "deactivate".
if (updatedDataCall.ifname != currentDataCall.ifname) {
return "deactivate";
}
// For suggestedRetryTime, the value of INT32_MAX(0x7fffffff) means no retry.
if (message.suggestedRetryTime === INT32_MAX ||
this.isPermanentFail(message.status, message.errorMsg)) {
if (DEBUG) this.debug("Data call error: no retry needed.");
return;
// If any existing address is missing, report as "deactivate".
for (let i = 0; i < currentDataCall.addresses.length; i++) {
let address = currentDataCall.addresses[i];
if (updatedDataCall.addresses.indexOf(address) < 0) {
return "deactivate";
}
}
this.retry(message.suggestedRetryTime);
if (currentDataCall.addresses.length != updatedDataCall.addresses.length) {
// Since now all |currentDataCall.addresses| are found in
// |updatedDataCall.addresses|, this means one or more new addresses are
// reported.
return "changed";
}
let fields = ["gateways", "dnses"];
for (let i = 0; i < fields.length; i++) {
// Compare <datacall>.<field>.
let field = fields[i];
let lhs = updatedDataCall[field], rhs = currentDataCall[field];
if (lhs.length != rhs.length) {
return "changed";
}
for (let i = 0; i < lhs.length; i++) {
if (lhs[i] != rhs[i]) {
return "changed";
}
}
}
return "identical";
},
dataCallStateChanged: function(datacall) {
if (DEBUG) {
this.debug("Data call ID: " + datacall.cid + ", interface name: " +
datacall.ifname + ", APN name: " + datacall.apn + ", state: " +
datacall.state);
onSetupDataCallResult: function(dataCall) {
if (dataCall.status && dataCall.status != RIL.DATACALL_FAIL_NONE) {
dataCall.errorMsg =
RIL.RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[dataCall.status];
}
if (this.state == datacall.state &&
datacall.state != RIL.GECKO_NETWORK_STATE_CONNECTED) {
if (dataCall.errorMsg) {
if (DEBUG) {
this.debug("SetupDataCall error for apn " + dataCall.apn + ": " +
dataCall.errorMsg + " (" + dataCall.status + "), retry time: " +
dataCall.suggestedRetryTime);
}
this.state = RIL.GECKO_NETWORK_STATE_DISCONNECTED;
if (this.requestedNetworkIfaces.length === 0) {
if (DEBUG) this.debug("This DataCall is not requested anymore.");
return;
}
// Let DataConnectionHandler notify MobileConnectionService
let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
connHandler.notifyDataCallError(this);
// For suggestedRetryTime, the value of INT32_MAX(0x7fffffff) means no retry.
if (dataCall.suggestedRetryTime === INT32_MAX ||
this.isPermanentFail(dataCall.status, dataCall.errorMsg)) {
if (DEBUG) this.debug("Data call error: no retry needed.");
return;
}
this.retry(dataCall.suggestedRetryTime);
return;
}
switch (datacall.state) {
case RIL.GECKO_NETWORK_STATE_CONNECTED:
if (this.state == RIL.GECKO_NETWORK_STATE_CONNECTING) {
this.apnRetryCounter = 0;
this.linkInfo.cid = datacall.cid;
this.apnRetryCounter = 0;
this.linkInfo.cid = dataCall.cid;
if (this.requestedNetworkIfaces.length === 0) {
if (DEBUG) {
this.debug("State is connected, but no network interface requested" +
" this DataCall");
}
if (this.requestedNetworkIfaces.length === 0) {
if (DEBUG) {
this.debug("State is connected, but no network interface requested" +
" this DataCall");
}
this.deactivate();
return;
}
this.linkInfo.ifname = dataCall.ifname;
this.linkInfo.addresses = dataCall.addresses.slice();
this.linkInfo.gateways = dataCall.gateways.slice();
this.linkInfo.dnses = dataCall.dnses.slice();
this.state = dataCall.state;
// Notify DataConnectionHandler about data call connected.
let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
connHandler.notifyDataCallChanged(this);
for (let i = 0; i < this.requestedNetworkIfaces.length; i++) {
this.requestedNetworkIfaces[i].notifyRILNetworkInterface();
}
},
onDeactivateDataCallResult: function() {
this.reset();
if (this.requestedNetworkIfaces.length > 0) {
if (DEBUG) {
this.debug("State is disconnected/unknown, but this DataCall is" +
" requested.");
}
this.setup();
return;
}
// Notify DataConnectionHandler about data call disconnected.
let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
connHandler.notifyDataCallChanged(this);
},
onDataCallChanged: function(updatedDataCall) {
if (DEBUG) {
this.debug("onDataCallChanged: " + JSON.stringify(updatedDataCall));
}
if (this.state == updatedDataCall.state &&
updatedDataCall.state != RIL.GECKO_NETWORK_STATE_CONNECTED) {
return;
}
switch (updatedDataCall.state) {
case RIL.GECKO_NETWORK_STATE_CONNECTED:
if (this.state == RIL.GECKO_NETWORK_STATE_CONNECTED) {
let result =
this._compareDataCallLink(updatedDataCall, this.linkInfo);
if (result == "identical") {
if (DEBUG) this.debug("No changes in data call.");
return;
}
if (result == "deactivate") {
if (DEBUG) this.debug("Data link changed, cleanup.");
this.deactivate();
return;
}
this.linkInfo.ifname = datacall.ifname;
for (let entry of datacall.addresses) {
this.linkInfo.ips.push(entry.address);
this.linkInfo.prefixLengths.push(entry.prefixLength);
}
this.linkInfo.gateways = datacall.gateways.slice();
this.linkInfo.dnses = datacall.dnses.slice();
} else if (this.state == RIL.GECKO_NETWORK_STATE_CONNECTED) {
// configuration changed.
let changed = false;
if (this.linkInfo.ips.length != datacall.addresses.length) {
changed = true;
this.linkInfo.ips = [];
this.linkInfo.prefixLengths = [];
for (let entry of datacall.addresses) {
this.linkInfo.ips.push(entry.address);
this.linkInfo.prefixLengths.push(entry.prefixLength);
}
// Minor change, just update and notify.
if (DEBUG) {
this.debug("Data link minor change, just update and notify.");
}
let reduceFunc = function(aRhs, aChanged, aElement, aIndex) {
return aChanged || (aElement != aRhs[aIndex]);
};
for (let field of ["gateways", "dnses"]) {
let lhs = this.linkInfo[field], rhs = datacall[field];
if (lhs.length != rhs.length ||
lhs.reduce(reduceFunc.bind(null, rhs), false)) {
changed = true;
this.linkInfo[field] = rhs.slice();
}
}
if (!changed) {
return;
}
this.linkInfo.addresses = updatedDataCall.addresses.slice();
this.linkInfo.gateways = updatedDataCall.gateways.slice();
this.linkInfo.dnses = updatedDataCall.dnses.slice();
}
break;
case RIL.GECKO_NETWORK_STATE_DISCONNECTED:
case RIL.GECKO_NETWORK_STATE_UNKNOWN:
if (this.state == RIL.GECKO_NETWORK_STATE_CONNECTED) {
// Notify first on unexpected data call disconnection.
this.state = datacall.state;
this.state = updatedDataCall.state;
for (let i = 0; i < this.requestedNetworkIfaces.length; i++) {
this.requestedNetworkIfaces[i].notifyRILNetworkInterface();
}
@@ -2784,7 +2601,12 @@ DataCall.prototype = {
break;
}
this.state = datacall.state;
this.state = updatedDataCall.state;
// Notify DataConnectionHandler about data call changed.
let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
connHandler.notifyDataCallChanged(this);
for (let i = 0; i < this.requestedNetworkIfaces.length; i++) {
this.requestedNetworkIfaces[i].notifyRILNetworkInterface();
}
@@ -2851,8 +2673,7 @@ DataCall.prototype = {
reset: function() {
this.linkInfo.cid = null;
this.linkInfo.ifname = null;
this.linkInfo.ips = [];
this.linkInfo.prefixLengths = [];
this.linkInfo.addresses = [];
this.linkInfo.dnses = [];
this.linkInfo.gateways = [];
@@ -2946,7 +2767,7 @@ DataCall.prototype = {
passwd: this.apnProfile.password,
chappap: authType,
pdptype: pdpType
});
}, this.onSetupDataCallResult.bind(this));
this.state = RIL.GECKO_NETWORK_STATE_CONNECTING;
},
@@ -3031,7 +2852,8 @@ DataCall.prototype = {
radioInterface.sendWorkerMessage("deactivateDataCall", {
cid: this.linkInfo.cid,
reason: reason
});
}, this.onDeactivateDataCallResult.bind(this));
this.state = RIL.GECKO_NETWORK_STATE_DISCONNECTING;
},
@@ -3101,29 +2923,37 @@ RILNetworkInterface.prototype = {
return this.apnSetting.port || "";
},
getAddresses: function(ips, prefixLengths) {
let linkInfo = this.dataCall.linkInfo;
getAddresses: function(aIps, aPrefixLengths) {
let addresses = this.dataCall.linkInfo.addresses;
ips.value = linkInfo.ips.slice();
prefixLengths.value = linkInfo.prefixLengths.slice();
let ips = [];
let prefixLengths = [];
for (let i = 0; i < addresses.length; i++) {
let [ip, prefixLength] = addresses[i].split("/");
ips.push(ip);
prefixLengths.push();
}
return linkInfo.ips.length;
aIps.value = ips.slice();
aPrefixLengths.value = prefixLengths.slice();
return aIps.length;
},
getGateways: function(count) {
getGateways: function(aCount) {
let linkInfo = this.dataCall.linkInfo;
if (count) {
count.value = linkInfo.gateways.length;
if (aCount) {
aCount.value = linkInfo.gateways.length;
}
return linkInfo.gateways.slice();
},
getDnses: function(count) {
getDnses: function(aCount) {
let linkInfo = this.dataCall.linkInfo;
if (count) {
count.value = linkInfo.dnses.length;
if (aCount) {
aCount.value = linkInfo.dnses.length;
}
return linkInfo.dnses.slice();
},
@@ -3137,8 +2967,8 @@ RILNetworkInterface.prototype = {
},
get iccId() {
let iccInfo = this.dataConnectionHandler.radioInterface.rilContext.iccInfo;
return iccInfo && iccInfo.iccid;
let iccInfo = this.dataConnectionHandler.radioInterface.getIccInfo();
return iccInfo ? iccInfo.iccid : null;
},
get mmsc() {
+6
View File
@@ -88,6 +88,9 @@ EXTRA_JS_MODULES += [
if CONFIG['MOZ_B2G_RIL']:
XPIDL_SOURCES += [
'nsIDataCallInterfaceService.idl',
'nsIDataCallManager.idl',
'nsIGonkDataCallInterfaceService.idl',
'nsIRadioInterfaceLayer.idl',
]
EXTRA_COMPONENTS += [
@@ -100,10 +103,13 @@ if CONFIG['MOZ_B2G_RIL']:
'ril_consts.js',
'ril_worker.js',
'ril_worker_buf_object.js',
'ril_worker_telephony_request_queue.js',
'RILSystemMessenger.jsm',
]
if not CONFIG['DISABLE_MOZ_RIL_GEOLOC']:
EXTRA_COMPONENTS += [
'DataCallInterfaceService.js',
'DataCallInterfaceService.manifest',
'RadioInterfaceLayer.js',
'RadioInterfaceLayer.manifest',
]
@@ -0,0 +1,257 @@
/* 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 "nsISupports.idl"
[scriptable, uuid(d27ce247-9c7c-4582-826b-125e8275e9c2)]
interface nsIDataCall : nsISupports
{
/**
* Data call fail cause. One of the nsIDataCallInterface.DATACALL_FAIL_*
* values.
*/
readonly attribute long failCause;
/**
* If failCause != nsIDataCallInterface.DATACALL_FAIL_NONE, this field
* indicates the suggested retry back-off timer. The unit is milliseconds.
*/
readonly attribute long suggestedRetryTime;
/**
* Context ID, uniquely identifies this call.
*/
readonly attribute long cid;
/**
* Data call network state. One of the nsIDataCallInterface.DATACALL_STATE_*
* values.
*/
readonly attribute long active;
/**
* Data call connection type. One of the
* nsIDataCallInterface.DATACALL_PDP_TYPE_* values.
*/
readonly attribute long pdpType;
/**
* The network interface name.
*/
readonly attribute DOMString ifname;
/**
* A space-delimited list of addresses with optional "/" prefix length.
*/
readonly attribute DOMString addresses;
/**
* A space-delimited list of DNS server addresses.
*/
readonly attribute DOMString dnses;
/**
* A space-delimited list of default gateway addresses.
*/
readonly attribute DOMString gateways;
};
[scriptable, uuid(e119c54b-9354-4ad6-a1ee-18608bde9320)]
interface nsIDataCallInterfaceListener : nsISupports
{
/**
* Notify data call interface listeners about unsolicited data call state
* changes.
*/
void notifyDataCallListChanged(in uint32_t count,
[array, size_is(count)] in nsIDataCall
dataCalls);
};
[scriptable, uuid(db0b640a-3b3a-4f48-84dc-256e176876c2)]
interface nsIDataCallCallback : nsISupports
{
/**
* Called when setupDataCall() returns succesfully.
*/
void notifySetupDataCallSuccess(in nsIDataCall dataCall);
/**
* Called when getDataCallList() returns succesfully.
*/
void notifyGetDataCallListSuccess(in uint32_t count,
[array, size_is(count)] in nsIDataCall
dataCalls);
/**
* Called when request returns succesfully.
*/
void notifySuccess();
/**
* Called when request returns error.
*/
void notifyError(in AString errorMsg);
};
[scriptable, uuid(ec219021-8623-4b9f-aba5-4db58c60684f)]
interface nsIDataCallInterface : nsISupports
{
/**
* Data fail causes, defined in TS 24.008.
*/
const long DATACALL_FAIL_NONE = 0;
const long DATACALL_FAIL_OPERATOR_BARRED = 0x08;
const long DATACALL_FAIL_INSUFFICIENT_RESOURCES = 0x1A;
const long DATACALL_FAIL_MISSING_UKNOWN_APN = 0x1B;
const long DATACALL_FAIL_UNKNOWN_PDP_ADDRESS_TYPE = 0x1C;
const long DATACALL_FAIL_USER_AUTHENTICATION = 0x1D;
const long DATACALL_FAIL_ACTIVATION_REJECT_GGSN = 0x1E;
const long DATACALL_FAIL_ACTIVATION_REJECT_UNSPECIFIED = 0x1F;
const long DATACALL_FAIL_SERVICE_OPTION_NOT_SUPPORTED = 0x20;
const long DATACALL_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED = 0x21;
const long DATACALL_FAIL_SERVICE_OPTION_OUT_OF_ORDER = 0x22;
const long DATACALL_FAIL_NSAPI_IN_USE = 0x23;
const long DATACALL_FAIL_ONLY_IPV4_ALLOWED = 0x32;
const long DATACALL_FAIL_ONLY_IPV6_ALLOWED = 0x33;
const long DATACALL_FAIL_ONLY_SINGLE_BEARER_ALLOWED = 0x34;
const long DATACALL_FAIL_PROTOCOL_ERRORS = 0x6F;
/* Not mentioned in the specification */
const long DATACALL_FAIL_VOICE_REGISTRATION_FAIL = -1;
const long DATACALL_FAIL_DATA_REGISTRATION_FAIL = -2;
const long DATACALL_FAIL_SIGNAL_LOST = -3;
const long DATACALL_FAIL_PREF_RADIO_TECH_CHANGED = -4;
const long DATACALL_FAIL_RADIO_POWER_OFF = -5;
const long DATACALL_FAIL_TETHERED_CALL_ACTIVE = -6;
const long DATACALL_FAIL_ERROR_UNSPECIFIED = 0xFFFF;
/**
* Data call network state.
*/
const long DATACALL_STATE_INACTIVE = 0;
const long DATACALL_STATE_ACTIVE_DOWN = 1;
const long DATACALL_STATE_ACTIVE_UP = 2;
/**
* Data call authentication type. Must match the values in ril_consts
* RIL_DATACALL_AUTH_TO_GECKO array.
*/
const long DATACALL_AUTH_NONE = 0;
const long DATACALL_AUTH_PAP = 1;
const long DATACALL_AUTH_CHAP = 2;
const long DATACALL_AUTH_PAP_OR_CHAP = 3;
/**
* Data call protocol type. Must match the values in ril_consts
* RIL_DATACALL_PDP_TYPES array.
*/
const long DATACALL_PDP_TYPE_IPV4 = 0;
const long DATACALL_PDP_TYPE_IPV4V6 = 1;
const long DATACALL_PDP_TYPE_IPV6 = 2;
/**
* Reason for deactivating data call.
*/
const long DATACALL_DEACTIVATE_NO_REASON = 0;
const long DATACALL_DEACTIVATE_RADIO_SHUTDOWN = 1;
/**
* Setup data call.
*
* @param apn
* Apn to connect to.
* @param username
* Username for apn.
* @param password
* Password for apn.
* @param authType
* Authentication type. One of the DATACALL_AUTH_* values.
* @param pdpType
* Connection type. One of the DATACALL_PDP_TYPE_* values.
* @param nsIDataCallCallback
* Called when request is finished.
*
* If successful, the notifySetupDataCallSuccess() will be called with the
* new nsIDataCall.
*
* Otherwise, the notifyError() will be called, and the error will be either
* 'RadioNotAvailable', 'OpNotAllowedBeforeRegToNw',
* 'OpNotAllowedDuringVoiceCall', 'RequestNotSupported' or 'GenericFailure'.
*/
void setupDataCall(in AString apn, in AString username,
in AString password, in long authType,
in long pdpType,
in nsIDataCallCallback callback);
/**
* Deactivate data call.
*
* @param cid
* Context id.
* @param reason
* Disconnect Reason. One of the DATACALL_DEACTIVATE_* values.
* @param nsIDataCallCallback
* Called when request is finished.
*
* If successful, the notifySuccess() will be called.
*
* Otherwise, the notifyError() will be called, and the error will be either
* 'RadioNotAvailable' or 'GenericFailure'.
*/
void deactivateDataCall(in long cid,
in long reason,
in nsIDataCallCallback callback);
/**
* Get current data call list.
*
* @param nsIDataCallCallback
* Called when request is finished.
*
* If successful, the notifyGetDataCallListSuccess() will be called with the
* list of nsIDataCall(s).
*
* Otherwise, the notifyError() will be called, and the error will be either
* 'RadioNotAvailable' or 'GenericFailure'.
*/
void getDataCallList(in nsIDataCallCallback callback);
/**
* Set data registration state.
*
* @param attach
* whether to attach data registration or not.
* @param nsIDataCallCallback
* Called when request is finished.
*
* If successful, the notifySuccess() will be called.
*
* Otherwise, the notifyError() will be called, and the error will be either
* 'RadioNotAvailable', 'SubscriptionNotAvailable' or 'GenericFailure'.
*/
void setDataRegistration(in boolean attach,
in nsIDataCallCallback callback);
/**
* Register to receive unsolicited events from this nsIDataCallInterface.
*/
void registerListener(in nsIDataCallInterfaceListener listener);
/**
* Unregister to stop receiving unsolicited events from this
* nsIDataCallInterface.
*/
void unregisterListener(in nsIDataCallInterfaceListener listener);
};
[scriptable, uuid(64700406-7429-4743-a6ae-f82e9877fd0d)]
interface nsIDataCallInterfaceService : nsISupports
{
/**
* Get the corresponding data call interface.
*
* @param clientId
* clientId of the data call interface to get.
*/
nsIDataCallInterface getDataCallInterface(in long clientId);
};
+68
View File
@@ -0,0 +1,68 @@
/* 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 "nsISupports.idl"
#include "nsINetworkManager.idl"
[scriptable, uuid(e48d290b-ea3b-4987-9333-2e01f64c92ba)]
interface nsIRilNetworkInterface : nsINetworkInterface
{
readonly attribute unsigned long serviceId;
readonly attribute DOMString iccId;
/* The following attributes are for MMS proxy settings. */
readonly attribute DOMString mmsc; // Empty string if not set.
readonly attribute DOMString mmsProxy; // Empty string if not set.
readonly attribute long mmsPort; // -1 if not set.
};
[scriptable, function, uuid(cb2f0f5b-67f4-4c14-93e8-01e66b630464)]
interface nsIDeactivateDataCallsCallback : nsISupports
{
/**
* Callback function used to notify when all data calls are disconnected.
*/
void notifyDataCallsDisconnected();
};
[scriptable, uuid(e3feec20-36b4-47de-a7a5-e32a65f20186)]
interface nsIDataCallHandler : nsISupports
{
/**
* PDP APIs
*
* @param networkType
* Mobile network type, that is,
* nsINetworkInterface.NETWORK_TYPE_MOBILE or one of the
* nsINetworkInterface.NETWORK_TYPE_MOBILE_* values.
*/
void setupDataCallByType(in long networkType);
void deactivateDataCallByType(in long networkType);
long getDataCallStateByType(in long networkType);
/**
* Deactivate all data calls.
*
* @param callback
* Callback to notify when all data calls are disconnected.
*/
void deactivateDataCalls(in nsIDeactivateDataCallsCallback callback);
/**
* Called to reconsider data call state.
*/
void updateRILNetworkInterface();
};
[scriptable, uuid(aac54873-5771-4093-a72b-fe39967c6607)]
interface nsIDataCallManager : nsISupports
{
/**
* Get the corresponding data call handler.
*
* @param clientId
* clientId of the data call handler to get.
*/
nsIDataCallHandler getDataCallHandler(in unsigned long clientId);
};
@@ -0,0 +1,18 @@
/* 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 "nsIDataCallInterfaceService.idl"
[scriptable, uuid(f008d00c-e2b8-49b2-8f88-19111577938e)]
interface nsIGonkDataCallInterfaceService : nsIDataCallInterfaceService
{
/**
* Called by RadioInterface or lower layer to notify about data call list
* changes.
*/
void notifyDataCallListChanged(in unsigned long clientId,
in uint32_t count,
[array, size_is(count)] in nsIDataCall
dataCalls);
};
+1 -39
View File
@@ -3,48 +3,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsINetworkManager.idl"
interface nsIIccInfo;
interface nsIMobileConnectionInfo;
interface nsIMobileMessageCallback;
[scriptable, uuid(6e0f45b8-410e-11e3-8c8e-b715b2cd0128)]
interface nsIRilNetworkInterface : nsINetworkInterface
{
readonly attribute unsigned long serviceId;
readonly attribute DOMString iccId;
/* The following attributes are for MMS proxy settings. */
readonly attribute DOMString mmsc; // Empty string if not set.
readonly attribute DOMString mmsProxy; // Empty string if not set.
readonly attribute long mmsPort; // -1 if not set.
};
[scriptable, uuid(4441e660-4ad0-11e4-916c-0800200c9a66)]
interface nsIRilContext : nsISupports
{
/**
* One of the nsIIcc.CARD_STATE_* values.
*/
readonly attribute unsigned long cardState;
readonly attribute DOMString imsi;
readonly attribute nsIIccInfo iccInfo;
};
[scriptable, function, uuid(3bc96351-53b0-47a1-a888-c74c64b60f25)]
interface nsIRilSendWorkerMessageCallback : nsISupports
{
boolean handleResponse(in jsval response);
};
[scriptable, uuid(fe01c648-867a-11e4-915f-033b36e8177b)]
[scriptable, uuid(1a3ef88a-e4d1-11e4-8512-176220f2b32b)]
interface nsIRadioInterface : nsISupports
{
readonly attribute nsIRilContext rilContext;
/**
* PDP APIs
*
@@ -79,13 +51,3 @@ interface nsIRadioInterfaceLayer : nsISupports
void setMicrophoneMuted(in boolean muted);
};
/**
* Helper Interface to define new APIs of nsIRadioInterfaceLayer during
* ril-interfaces frozen phase.
*/
[scriptable, uuid(f8ec63da-c22e-11e4-89f3-b767dae42a13)]
interface nsIRadioInterfaceLayer_new : nsIRadioInterfaceLayer
{
};
-5
View File
@@ -494,11 +494,6 @@ this.CELL_INFO_TYPE_CDMA = 2;
this.CELL_INFO_TYPE_LTE = 3;
this.CELL_INFO_TYPE_WCDMA = 4;
// Order matters.
this.AUDIO_STATE_NO_CALL = 0;
this.AUDIO_STATE_INCOMING = 1;
this.AUDIO_STATE_IN_CALL = 2;
this.CALL_STATE_UNKNOWN = -1;
this.CALL_STATE_ACTIVE = 0;
this.CALL_STATE_HOLDING = 1;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,157 @@
/* global DEBUG, DEBUG_WORKER */
/* global REQUEST_GET_CURRENT_CALLS */
/* global REQUEST_ANSWER, REQUEST_CONFERENCE, REQUEST_DIAL */
/* global REQUEST_DIAL_EMERGENCY_CALL, REQUEST_HANGUP */
/* global REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND */
/* global REQUEST_HANGUP_WAITING_OR_BACKGROUND */
/* global REQUEST_SEPARATE_CONNECTION */
/* global REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, REQUEST_UDUB */
"use strict";
(function(exports) {
const TELEPHONY_REQUESTS = [
REQUEST_GET_CURRENT_CALLS,
REQUEST_ANSWER,
REQUEST_CONFERENCE,
REQUEST_DIAL,
REQUEST_DIAL_EMERGENCY_CALL,
REQUEST_HANGUP,
REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
REQUEST_HANGUP_WAITING_OR_BACKGROUND,
REQUEST_SEPARATE_CONNECTION,
REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE,
REQUEST_UDUB
];
// Set to true in ril_consts.js to see debug messages
let DEBUG = DEBUG_WORKER;
/**
* Queue entry; only used in the queue.
*/
let TelephonyRequestEntry = function(request, callback) {
this.request = request;
this.callback = callback;
};
let TelephonyRequestQueue = function(ril) {
this.ril = ril;
this.currentQueue = null; // Point to the current running queue.
this.queryQueue = [];
this.controlQueue = [];
};
TelephonyRequestQueue.prototype._getQueue = function(request) {
return (request === REQUEST_GET_CURRENT_CALLS) ? this.queryQueue
: this.controlQueue;
};
TelephonyRequestQueue.prototype._getAnotherQueue = function(queue) {
return (this.queryQueue === queue) ? this.controlQueue : this.queryQueue;
};
TelephonyRequestQueue.prototype._find = function(queue, request) {
for (let i = 0; i < queue.length; ++i) {
if (queue[i].request === request) {
return i;
}
}
return -1;
};
TelephonyRequestQueue.prototype._startQueue = function(queue) {
if (queue.length === 0) {
return;
}
// We only need to keep one entry for queryQueue.
if (queue === this.queryQueue) {
queue.splice(1, queue.length - 1);
}
this.currentQueue = queue;
for (let entry of queue) {
this._executeEntry(entry);
}
};
TelephonyRequestQueue.prototype._executeEntry = function(entry) {
if (DEBUG) {
this.debug("execute " + this._getRequestName(entry.request));
}
entry.callback();
};
TelephonyRequestQueue.prototype._getRequestName = function(request) {
let method = this.ril[request];
return (typeof method === 'function') ? method.name : "";
};
TelephonyRequestQueue.prototype.debug = function(msg) {
this.ril.context.debug("[TeleQ] " + msg);
};
TelephonyRequestQueue.prototype.isValidRequest = function(request) {
return TELEPHONY_REQUESTS.indexOf(request) !== -1;
};
TelephonyRequestQueue.prototype.push = function(request, callback) {
if (!this.isValidRequest(request)) {
if (DEBUG) {
this.debug("Error: " + this._getRequestName(request) +
" is not a telephony request");
}
return;
}
if (DEBUG) {
this.debug("push " + this._getRequestName(request));
}
let entry = new TelephonyRequestEntry(request, callback);
let queue = this._getQueue(request);
queue.push(entry);
// Try to run the request.
if (this.currentQueue === queue) {
this._executeEntry(entry);
} else if (!this.currentQueue) {
this._startQueue(queue);
}
};
TelephonyRequestQueue.prototype.pop = function(request) {
if (!this.isValidRequest(request)) {
if (DEBUG) {
this.debug("Error: " + this._getRequestName(request) +
" is not a telephony request");
}
return;
}
if (DEBUG) {
this.debug("pop " + this._getRequestName(request));
}
let queue = this._getQueue(request);
let index = this._find(queue, request);
if (index === -1) {
throw new Error("Cannot find the request in telephonyRequestQueue.");
} else {
queue.splice(index, 1);
}
if (queue.length === 0) {
this.currentQueue = null;
this._startQueue(this._getAnotherQueue(queue));
}
};
// Before we make sure to form it as a module would not add extra
// overhead of module loading, we need to define it in this way
// rather than 'module.exports' it as a module component.
exports.TelephonyRequestQueue = TelephonyRequestQueue;
})(self); // in worker self is the global
-18
View File
@@ -78,18 +78,6 @@ this.DialNumberUtils = {
return new RegExp("^" + fullmmi + optionalDialString + "$");
})(),
_isPoundString: function(aString) {
return aString && aString[aString.length - 1] === "#";
},
_isShortString: function(aString) {
if (!aString || this.isEmergency(aString) || aString.length > 2 ||
(aString.length == 2 && aString[0] === "1")) {
return false;
}
return true;
},
/**
* Check parse the given string as an MMI code.
*
@@ -117,12 +105,6 @@ this.DialNumberUtils = {
};
}
if (this._isPoundString(aString) || this._isShortString(aString)) {
return {
fullMMI: aString
};
}
return null;
}
};
+444 -169
View File
@@ -157,6 +157,26 @@ TelephonyCallInfo.prototype = {
isMergeable: true
};
function Call(aClientId, aCallIndex) {
this.clientId = aClientId;
this.callIndex = aCallIndex;
}
Call.prototype = {
clientId: 0,
callIndex: 0,
state: nsITelephonyService.CALL_STATE_UNKNOWN,
number: "",
numberPresentation: nsITelephonyService.CALL_PRESENTATION_ALLOWED,
name: "",
namePresentation: nsITelephonyService.CALL_PRESENTATION_ALLOWED,
isOutgoing: true,
isEmergency: false,
isConference: false,
isSwitchable: true,
isMergeable: true,
started: null
};
function TelephonyService() {
this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
this._listeners = [];
@@ -165,7 +185,7 @@ function TelephonyService() {
this._cachedDialRequest = null;
this._currentCalls = {};
this._currentConferenceState = nsITelephonyService.CALL_STATE_UNKNOWN;
this._audioStates = {};
this._audioStates = [];
this._cdmaCallWaitingNumber = null;
@@ -178,8 +198,9 @@ function TelephonyService() {
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
for (let i = 0; i < this._numClients; ++i) {
this._audioStates[i] = nsITelephonyAudioService.PHONE_STATE_NORMAL;
this._currentCalls[i] = {};
this._enumerateCallsForClient(i);
this._audioStates[i] = RIL.AUDIO_STATE_NO_CALL;
}
}
TelephonyService.prototype = {
@@ -285,18 +306,39 @@ TelephonyService.prototype = {
}
},
_updateAudioState: function(aAudioState) {
switch (aAudioState) {
case RIL.AUDIO_STATE_NO_CALL:
gAudioService.setPhoneState(nsITelephonyAudioService.PHONE_STATE_NORMAL);
break;
case RIL.AUDIO_STATE_INCOMING:
gAudioService.setPhoneState(nsITelephonyAudioService.PHONE_STATE_RINGTONE);
break;
case RIL.AUDIO_STATE_IN_CALL:
gAudioService.setPhoneState(nsITelephonyAudioService.PHONE_STATE_IN_CALL);
break;
_computeAudioStateForClient: function(aClientId) {
let indexes = Object.keys(this._currentCalls[aClientId]);
if (!indexes.length) {
return nsITelephonyAudioService.PHONE_STATE_NORMAL;
}
let firstCall = this._currentCalls[aClientId][indexes[0]];
if (indexes.length === 1 &&
firstCall.state === nsITelephonyService.CALL_STATE_INCOMING) {
return nsITelephonyAudioService.PHONE_STATE_RINGTONE;
}
return nsITelephonyAudioService.PHONE_STATE_IN_CALL;
},
_updateAudioState: function(aClientId) {
this._audioStates[aClientId] = this._computeAudioStateForClient(aClientId);
if (this._audioStates.some(state => state === nsITelephonyAudioService.PHONE_STATE_IN_CALL)) {
gAudioService.setPhoneState(nsITelephonyAudioService.PHONE_STATE_IN_CALL);
} else if (this._audioStates.some(state => state === nsITelephonyAudioService.PHONE_STATE_RINGTONE)) {
gAudioService.setPhoneState(nsITelephonyAudioService.PHONE_STATE_RINGTONE);
} else {
gAudioService.setPhoneState(nsITelephonyAudioService.PHONE_STATE_NORMAL);
}
},
_formatInternationalNumber: function(aNumber, aToa) {
if (aNumber && aToa == RIL.TOA_INTERNATIONAL && aNumber[0] != "+") {
return "+" + aNumber;
}
return aNumber;
},
_convertRILCallState: function(aState) {
@@ -359,17 +401,17 @@ TelephonyService.prototype = {
_enumerateCallsForClient: function(aClientId) {
if (DEBUG) debug("Enumeration of calls for client " + aClientId);
this._sendToRilWorker(aClientId, "enumerateCalls", null, response => {
if (!this._currentCalls[aClientId]) {
this._currentCalls[aClientId] = {};
this._sendToRilWorker(aClientId, "getCurrentCalls", null, response => {
if (response.errorMsg) {
return;
}
for (let call of response.calls) {
call.clientId = aClientId;
call.state = this._convertRILCallState(call.state);
call.isSwitchable = true;
call.isMergeable = true;
this._currentCalls[aClientId][call.callIndex] = call;
// Clear all.
this._currentCalls[aClientId] = {};
for (let i in response.calls) {
let call = this._currentCalls[aClientId][i] = new Call(aClientId, i);
this._updateCallFromRil(call, response.calls[i]);
}
});
},
@@ -474,18 +516,23 @@ TelephonyService.prototype = {
},
/**
* Get arbitrary one of active call.
* Is there an active call?
*/
_getOneActiveCall: function(aClientId) {
_isActive: function(aClientId) {
for (let index in this._currentCalls[aClientId]) {
let call = this._currentCalls[aClientId][index];
if (call.state === nsITelephonyService.CALL_STATE_CONNECTED) {
return call;
return true;
}
}
return null;
return false;
},
/**
* Dial number. Perform call setup or SS procedure accordingly.
*
* @see 3GPP TS 22.030 Figure 3.5.3.2
*/
dial: function(aClientId, aNumber, aIsDialEmergency, aCallback) {
if (DEBUG) debug("Dialing " + (aIsDialEmergency ? "emergency " : "") + aNumber);
@@ -505,16 +552,21 @@ TelephonyService.prototype = {
let isEmergencyNumber = gDialNumberUtils.isEmergency(aNumber);
// Should be radio on except it's an emergency number.
if (!(this._isRadioOn(aClientId) || isEmergencyNumber)) {
aCallback.notifyError(DIAL_ERROR_RADIO_NOT_AVAILABLE);
// DialEmergency accepts only emergency number.
if (aIsDialEmergency && !isEmergencyNumber) {
if (!this._isRadioOn(aClientId)) {
if (DEBUG) debug("Error: Radio is off. Drop.");
aCallback.notifyError(DIAL_ERROR_RADIO_NOT_AVAILABLE);
return;
}
if (DEBUG) debug("Error: Dial a non-emergency by dialEmergency. Drop.");
aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
return;
}
// DialEmergency accepts only emergency number.
if (aIsDialEmergency && !isEmergencyNumber) {
if (DEBUG) debug("Error: Dail a non-emergency by dialEmergency. Drop.");
aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
if (isEmergencyNumber) {
this._dialCall(aClientId, aNumber, undefined, aCallback);
return;
}
@@ -524,47 +576,56 @@ TelephonyService.prototype = {
return;
}
if (this._hasCalls(aClientId)) {
this._dialInCallMMI(aClientId, aNumber, aCallback);
return;
}
let mmi = gDialNumberUtils.parseMMI(aNumber);
if (!mmi) {
this._dialCall(aClientId, aNumber, undefined, aCallback);
} else if (this._isTemporaryCLIR(mmi)) {
this._dialCall(aClientId, mmi.dialNumber,
this._procedureToCLIRMode(mmi.procedure), aCallback);
if (mmi) {
if (this._isTemporaryCLIR(mmi)) {
this._dialCall(aClientId, mmi.dialNumber,
this._procedureToCLIRMode(mmi.procedure), aCallback);
} else {
this._dialMMI(aClientId, mmi, aCallback);
}
} else {
this._dialMMI(aClientId, mmi, aCallback);
if (aNumber[aNumber.length - 1] === "#") { // # string
this._dialMMI(aClientId, {fullMMI: aNumber}, aCallback);
} else if (aNumber.length <= 2) { // short string
if (this._hasCalls(aClientId)) {
this._dialInCallMMI(aClientId, aNumber, aCallback);
} else if (aNumber.length === 2 && aNumber[0] === "1") {
this._dialCall(aClientId, aNumber, undefined, aCallback);
} else {
this._dialMMI(aClientId, {fullMMI: aNumber}, aCallback);
}
} else {
this._dialCall(aClientId, aNumber, undefined, aCallback);
}
}
},
// Handling of supplementary services within a call as 3GPP TS 22.030 6.5.5
_dialInCallMMI: function(aClientId, aNumber, aCallback) {
let mmiCallback = response => {
aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL);
if (!response.success) {
aCallback.notifyDialMMIError(RIL.MMI_ERROR_KS_ERROR);
} else {
aCallback.notifyDialMMISuccess(RIL.MMI_SM_KS_CALL_CONTROL);
}
let mmiCallback = {
notifyError: () => aCallback.notifyDialMMIError(RIL.MMI_ERROR_KS_ERROR),
notifySuccess: () => aCallback.notifyDialMMISuccess(RIL.MMI_SM_KS_CALL_CONTROL)
};
if (aNumber === "0") {
this._sendToRilWorker(aClientId, "hangUpBackground", null, mmiCallback);
aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL);
this._hangUpBackground(aClientId, mmiCallback);
} else if (aNumber === "1") {
this._sendToRilWorker(aClientId, "hangUpForeground", null, mmiCallback);
aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL);
this._hangUpForeground(aClientId, mmiCallback);
} else if (aNumber[0] === "1" && aNumber.length === 2) {
this._sendToRilWorker(aClientId, "hangUpCall",
{ callIndex: parseInt(aNumber[1]) }, mmiCallback);
aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL);
this.hangUpCall(aClientId, parseInt(aNumber[1]), mmiCallback);
} else if (aNumber === "2") {
this._sendToRilWorker(aClientId, "switchActiveCall", null, mmiCallback);
aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL);
this._switchActiveCall(aClientId, mmiCallback);
} else if (aNumber[0] === "2" && aNumber.length === 2) {
this._sendToRilWorker(aClientId, "separateCall",
{ callIndex: parseInt(aNumber[1]) }, mmiCallback);
aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL);
this._separateCallGsm(aClientId, parseInt(aNumber[1]), mmiCallback);
} else if (aNumber === "3") {
this._sendToRilWorker(aClientId, "conferenceCall", null, mmiCallback);
aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL);
this._conferenceCallGsm(aClientId, mmiCallback);
} else {
this._dialCall(aClientId, aNumber, undefined, aCallback);
}
@@ -594,9 +655,19 @@ TelephonyService.prototype = {
}
let isEmergency = gDialNumberUtils.isEmergency(aNumber);
if (!isEmergency && this._isEmergencyOnly()) {
if (DEBUG) debug("Error: Dail a normal call when emergencyCallsOnly. Drop");
aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
if (!isEmergency) {
if (!this._isRadioOn(aClientId)) {
if (DEBUG) debug("Error: Dial a normal call when radio off. Drop");
aCallback.notifyError(DIAL_ERROR_RADIO_NOT_AVAILABLE);
return;
}
if (this._isEmergencyOnly()) {
if (DEBUG) debug("Error: Dial a normal call when emergencyCallsOnly. Drop");
aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
return;
}
}
if (isEmergency) {
@@ -616,8 +687,7 @@ TelephonyService.prototype = {
};
// No active call. Dial it out directly.
let activeCall = this._getOneActiveCall(aClientId);
if (!activeCall) {
if (!this._isActive(aClientId)) {
this._sendDialCallRequest(aClientId, options, aCallback);
return;
}
@@ -637,7 +707,7 @@ TelephonyService.prototype = {
return;
}
let autoHoldCallback = {
this._switchActiveCall(aClientId, {
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyCallback]),
notifySuccess: () => {
@@ -652,13 +722,7 @@ TelephonyService.prototype = {
if (DEBUG) debug("Error: Fail to automatically hold the active call.");
aCallback.notifyError(aErrorMsg);
}
};
if (activeCall.isConference) {
this.holdConference(aClientId, autoHoldCallback);
} else {
this.holdCall(aClientId, activeCall.callIndex, autoHoldCallback);
}
});
},
_dialCdmaThreeWayCall: function(aClientId, aNumber, aCallback) {
@@ -673,30 +737,30 @@ TelephonyService.prototype = {
aCallback.notifyDialCallSuccess(aClientId, CDMA_SECOND_CALL_INDEX,
aNumber);
let childCall = {
callIndex: CDMA_SECOND_CALL_INDEX,
state: RIL.CALL_STATE_DIALING,
number: aNumber,
isOutgoing: true,
isEmergency: false,
isConference: false,
isSwitchable: false,
isMergeable: true,
parentId: CDMA_FIRST_CALL_INDEX
};
let childCall = this._currentCalls[aClientId][CDMA_SECOND_CALL_INDEX] =
new Call(aClientId, CDMA_SECOND_CALL_INDEX);
childCall.parentId = CDMA_FIRST_CALL_INDEX;
childCall.state = nsITelephonyService.CALL_STATE_DIALING;
childCall.number = aNumber;
childCall.isOutgoing = true;
childCall.isEmergency = gDialNumberUtils.isEmergency(aNumber);
childCall.isConference = false;
childCall.isSwitchable = false;
childCall.isMergeable = true;
// Manual update call state according to the request response.
this.notifyCallStateChanged(aClientId, childCall);
this._handleCallStateChanged(aClientId, childCall);
childCall.state = RIL.CALL_STATE_ACTIVE;
this.notifyCallStateChanged(aClientId, childCall);
childCall.state = nsITelephonyService.CALL_STATE_CONNECTED;
this._handleCallStateChanged(aClientId, childCall);
let parentCall = this._currentCalls[aClientId][childCall.parentId];
parentCall.childId = CDMA_SECOND_CALL_INDEX;
parentCall.state = RIL.CALL_STATE_HOLDING;
parentCall.state = nsITelephonyService.CALL_STATE_HELD;
parentCall.isSwitchable = false;
parentCall.isMergeable = true;
this.notifyCallStateChanged(aClientId, parentCall);
this._handleCallStateChanged(aClientId, parentCall);
});
},
@@ -707,12 +771,15 @@ TelephonyService.prototype = {
this._isDialing = false;
if (!response.success) {
aCallback.notifyError(response.errorMsg);
return;
this._sendToRilWorker(aClientId, "getFailCause", null, response => {
aCallback.notifyError(response.failCause);
});
} else {
this._ongoingDial = {
clientId: aClientId,
callback: aCallback
};
}
aCallback.notifyDialCallSuccess(aClientId, response.callIndex,
response.number);
});
},
@@ -859,6 +926,76 @@ TelephonyService.prototype = {
}
},
_getCallsWithState: function(aClientId, aState) {
let calls = [];
for (let i in this._currentCalls[aClientId]) {
let call = this._currentCalls[aClientId][i];
if (call.state === aState) {
calls.push(call);
}
}
return calls;
},
/**
* Update call information from RIL.
*
* @return Boolean to indicate whether the data is changed.
*/
_updateCallFromRil: function(aCall, aRilCall) {
aRilCall.state = this._convertRILCallState(aRilCall.state);
aRilCall.number = this._formatInternationalNumber(aRilCall.number,
aRilCall.toa);
if (!aCall.started &&
aCall.state == nsITelephonyService.CALL_STATE_CONNECTED) {
aCall.started = new Date().getTime();
}
let change = false;
const key = ["state", "number", "numberPresentation", "name",
"namePresentation"];
for (let k of key) {
if (aCall[k] != aRilCall[k]) {
aCall[k] = aRilCall[k];
change = true;
}
}
aCall.isOutgoing = !aRilCall.isMT;
aCall.isEmergency = gDialNumberUtils.isEmergency(aCall.number);
return change;
},
/**
* Identify the conference group.
* Return the conference state and a array of calls in group.
*
* TODO: handle multi-sim case.
*/
_detectConference: function(aClientId) {
// There are some difficuties to identify the conference by |.isMpty| from RIL
// so we don't rely on this flag.
// - |.isMpty| becomes false when the conference call is put on hold.
// - |.isMpty| may remain true when other participants left the conference.
// All the calls in the conference should have the same state and it is
// either CONNECTED or HELD. That means, if we find a group of call with
// the same state and its size is larger than 2, it must be a conference.
let connectedCalls = this._getCallsWithState(aClientId, nsITelephonyService.CALL_STATE_CONNECTED);
let heldCalls = this._getCallsWithState(aClientId, nsITelephonyService.CALL_STATE_HELD);
if (connectedCalls.length >= 2) {
return [nsITelephonyService.CALL_STATE_CONNECTED, connectedCalls];
} else if (heldCalls.length >= 2) {
return [nsITelephonyService.CALL_STATE_HELD, heldCalls];
}
return [nsITelephonyService.CALL_STATE_UNKNOWN, null];
},
sendTones: function(aClientId, aDtmfChars, aPauseDuration, aToneDuration,
aCallback) {
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@@ -898,12 +1035,63 @@ TelephonyService.prototype = {
},
answerCall: function(aClientId, aCallIndex, aCallback) {
this._sendToRilWorker(aClientId, "answerCall", { callIndex: aCallIndex },
this._defaultCallbackHandler.bind(this, aCallback));
let call = this._currentCalls[aClientId][aCallIndex];
if (!call || call.state != nsITelephonyService.CALL_STATE_INCOMING) {
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
return;
}
let callNum = Object.keys(this._currentCalls[aClientId]).length;
if (callNum !== 1) {
this._switchActiveCall(aClientId, aCallback);
} else {
this._sendToRilWorker(aClientId, "answerCall", null,
this._defaultCallbackHandler.bind(this, aCallback));
}
},
rejectCall: function(aClientId, aCallIndex, aCallback) {
this._sendToRilWorker(aClientId, "rejectCall", { callIndex: aCallIndex },
if (this._isCdmaClient(aClientId)) {
this._hangUpBackground(aClientId, aCallback);
return;
}
let call = this._currentCalls[aClientId][aCallIndex];
if (!call || call.state != nsITelephonyService.CALL_STATE_INCOMING) {
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
return;
}
let callNum = Object.keys(this._currentCalls[aClientId]).length;
if (callNum !== 1) {
this._hangUpBackground(aClientId, aCallback);
} else {
this._sendToRilWorker(aClientId, "udub", null,
this._defaultCallbackHandler.bind(this, aCallback));
}
},
_hangUpForeground: function(aClientId, aCallback) {
let calls = this._getCallsWithState(aClientId, nsITelephonyService.CALL_STATE_CONNECTED);
calls.forEach(call => call.hangUpLocal = true);
this._sendToRilWorker(aClientId, "hangUpForeground", null,
this._defaultCallbackHandler.bind(this, aCallback));
},
_hangUpBackground: function(aClientId, aCallback) {
// When both a held and a waiting call exist, the request shall apply to
// the waiting call.
let waitingCalls = this._getCallsWithState(aClientId, nsITelephonyService.CALL_STATE_INCOMING);
let heldCalls = this._getCallsWithState(aClientId, nsITelephonyService.CALL_STATE_HELD);
if (waitingCalls.length) {
waitingCalls.forEach(call => call.hangUpLocal = true);
} else {
heldCalls.forEach(call => call.hangUpLocal = true);
}
this._sendToRilWorker(aClientId, "hangUpBackground", null,
this._defaultCallbackHandler.bind(this, aCallback));
},
@@ -912,6 +1100,13 @@ TelephonyService.prototype = {
// the parent call, we send 'parentId' to RIL.
aCallIndex = this._currentCalls[aClientId][aCallIndex].parentId || aCallIndex;
let call = this._currentCalls[aClientId][aCallIndex];
if (call.state === nsITelephonyService.CALL_STATE_HELD) {
this._hangUpBackground(aClientId, aCallback);
return;
}
call.hangUpLocal = true;
this._sendToRilWorker(aClientId, "hangUpCall", { callIndex: aCallIndex },
this._defaultCallbackHandler.bind(this, aCallback));
},
@@ -937,6 +1132,10 @@ TelephonyService.prototype = {
return;
}
this._switchActiveCall(aClientId, aCallback);
},
_switchActiveCall: function(aClientId, aCallback) {
this._sendToRilWorker(aClientId, "switchActiveCall", null,
this._defaultCallbackHandler.bind(this, aCallback));
},
@@ -996,11 +1195,11 @@ TelephonyService.prototype = {
for (let index in this._currentCalls[aClientId]) {
let call = this._currentCalls[aClientId][index];
call.state = RIL.CALL_STATE_ACTIVE;
call.state = nsITelephonyService.CALL_STATE_CONNECTED;
call.isConference = true;
this.notifyCallStateChanged(aClientId, call);
this._handleCallStateChanged(aClientId, call);
}
this.notifyConferenceCallStateChanged(RIL.CALL_STATE_ACTIVE);
this._handleConferenceCallStateChanged(nsITelephonyService.CALL_STATE_CONNECTED);
aCallback.notifySuccess();
});
@@ -1048,7 +1247,7 @@ TelephonyService.prototype = {
}
let childCall = this._currentCalls[aClientId][CDMA_SECOND_CALL_INDEX];
this.notifyCallDisconnected(aClientId, childCall);
this._handleCallDisconnected(aClientId, childCall);
aCallback.notifySuccess();
});
@@ -1091,8 +1290,7 @@ TelephonyService.prototype = {
return;
}
this._sendToRilWorker(aClientId, "switchActiveCall", null,
this._defaultCallbackHandler.bind(this, aCallback));
this._switchActiveCall(aClientId, aCallback);
},
holdConference: function(aClientId, aCallback) {
@@ -1133,26 +1331,13 @@ TelephonyService.prototype = {
* nsIGonkTelephonyService interface.
*/
notifyAudioStateChanged: function(aClientId, aState) {
this._audioStates[aClientId] = aState;
let audioState = aState;
for (let i = 0; i < this._numClients; ++i) {
audioState = Math.max(audioState, this._audioStates[i]);
}
this._updateAudioState(audioState);
},
/**
* Handle call disconnects by updating our current state and the audio system.
*/
notifyCallDisconnected: function(aClientId, aCall) {
_handleCallDisconnected: function(aClientId, aCall) {
if (DEBUG) debug("handleCallDisconnected: " + JSON.stringify(aCall));
aCall.clientId = aClientId;
aCall.state = nsITelephonyService.CALL_STATE_DISCONNECTED;
aCall.isEmergency = gDialNumberUtils.isEmergency(aCall.number);
let duration = ("started" in aCall && typeof aCall.started == "number") ?
new Date().getTime() - aCall.started : 0;
@@ -1172,7 +1357,7 @@ TelephonyService.prototype = {
if (childId) {
// Child cannot live without parent.
let childCall = this._currentCalls[aClientId][childId];
this.notifyCallDisconnected(aClientId, childCall);
this._handleCallDisconnected(aClientId, childCall);
} else {
let parentId = this._currentCalls[aClientId][aCall.callIndex].parentId;
if (parentId) {
@@ -1187,12 +1372,12 @@ TelephonyService.prototype = {
parentCall.isSwitchable = true;
parentCall.isMergeable = true;
aCall.isConference = false;
this.notifyCallStateChanged(aClientId, parentCall, true);
this._handleCallStateChanged(aClientId, parentCall);
}
}
}
if (!aCall.failCause ||
if (aCall.hangUpLocal || !aCall.failCause ||
aCall.failCause === RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) {
let callInfo = new TelephonyCallInfo(aCall);
this._notifyAllListeners("callStateChanged", [callInfo]);
@@ -1200,10 +1385,11 @@ TelephonyService.prototype = {
this._notifyAllListeners("notifyError",
[aClientId, aCall.callIndex, aCall.failCause]);
}
delete this._currentCalls[aClientId][aCall.callIndex];
if (manualConfStateChange) {
this.notifyConferenceCallStateChanged(RIL.CALL_STATE_UNKNOWN);
this._handleConferenceCallStateChanged(nsITelephonyService.CALL_STATE_UNKNOWN);
}
},
@@ -1222,56 +1408,125 @@ TelephonyService.prototype = {
},
/**
* Handle call state changes by updating our current state and the audio
* system.
* Handle current calls reported from RIL.
*
* @param aCalls call from RIL, which contains:
* state, callIndex, toa, isMT, number, numberPresentation, name,
* namePresentation.
*/
notifyCallStateChanged: function(aClientId, aCall, aSkipStateConversion) {
if (DEBUG) debug("handleCallStateChange: " + JSON.stringify(aCall));
notifyCurrentCalls: function(aClientId, aCalls) {
// Check whether there is a removed call.
let hasRemovedCalls = () => {
let newIndexes = new Set(Object.keys(aCalls));
for (let i in this._currentCalls[aClientId]) {
if (!newIndexes.has(i)) {
return true;
}
}
return false;
};
if (!aSkipStateConversion) {
aCall.state = this._convertRILCallState(aCall.state);
// If there are removedCalls, we should fetch the failCause first.
if (!hasRemovedCalls()) {
this._handleCurrentCalls(aClientId, aCalls);
} else {
this._sendToRilWorker(aClientId, "getFailCause", null, response => {
this._handleCurrentCalls(aClientId, aCalls, response.failCause);
});
}
},
_handleCurrentCalls: function(aClientId, aCalls,
aFailCause = RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) {
if (DEBUG) debug("handleCurrentCalls: " + JSON.stringify(aCalls) +
", failCause: " + aFailCause);
let changedCalls = new Set();
let removedCalls = new Set();
let allIndexes = new Set([...Object.keys(this._currentCalls[aClientId]),
...Object.keys(aCalls)]);
for (let i of allIndexes) {
let call = this._currentCalls[aClientId][i];
let rilCall = aCalls[i];
// Determine the change of call.
if (call && !rilCall) { // removed.
removedCalls.add(call);
} else if (call && rilCall) { // changed.
if (this._updateCallFromRil(call, rilCall)) {
changedCalls.add(call);
}
} else { // !call && rilCall. added.
this._currentCalls[aClientId][i] = call = new Call(aClientId, i);
this._updateCallFromRil(call, rilCall);
changedCalls.add(call);
// Handle ongoingDial.
if (this._ongoingDial && this._ongoingDial.clientId === aClientId &&
call.state !== nsITelephonyService.CALL_STATE_INCOMING) {
this._ongoingDial.callback.notifyDialCallSuccess(aClientId, i,
call.number);
this._ongoingDial = null;
}
}
}
// For correct conference detection, we should mark removedCalls as
// DISCONNECTED first.
removedCalls.forEach(call => {
call.state = nsITelephonyService.CALL_STATE_DISCONNECTED;
call.failCause = aFailCause;
this._handleCallDisconnected(aClientId, call);
});
// Detect conference and update isConference flag.
let [newConferenceState, conferenceCalls] = this._detectConference(aClientId);
if (DEBUG) debug("Conference state: " + newConferenceState);
let conference = new Set(conferenceCalls);
for (let i in this._currentCalls[aClientId]) {
let call = this._currentCalls[aClientId][i];
let isConference = conference.has(call);
if (call.isConference != isConference) {
call.isConference = isConference;
changedCalls.add(call);
}
}
changedCalls.forEach(call => this._handleCallStateChanged(aClientId, call));
// Should handle conferenceCallStateChange after callStateChanged and
// callDisconnected.
if (newConferenceState != this._currentConferenceState) {
this._handleConferenceCallStateChanged(newConferenceState);
}
this._updateAudioState(aClientId);
// Handle cached dial request.
if (this._cachedDialRequest && !this._isActive(aClientId)) {
if (DEBUG) debug("All calls held. Perform the cached dial request.");
let request = this._cachedDialRequest;
this._sendDialCallRequest(request.clientId, request.options,
request.callback);
this._cachedDialRequest = null;
}
},
/**
* Handle call state changes.
*/
_handleCallStateChanged: function(aClientId, aCall) {
if (DEBUG) debug("handleCallStateChange: " + JSON.stringify(aCall));
if (aCall.state == nsITelephonyService.CALL_STATE_DIALING) {
gTelephonyMessenger.notifyNewCall();
}
aCall.clientId = aClientId;
function pick(arg, defaultValue) {
return typeof arg !== 'undefined' ? arg : defaultValue;
}
let call = this._currentCalls[aClientId][aCall.callIndex];
if (call) {
call.state = aCall.state;
call.number = aCall.number;
call.isConference = aCall.isConference;
call.isEmergency = gDialNumberUtils.isEmergency(aCall.number);
call.isSwitchable = pick(aCall.isSwitchable, call.isSwitchable);
call.isMergeable = pick(aCall.isMergeable, call.isMergeable);
} else {
call = aCall;
call.isEmergency = pick(aCall.isEmergency, gDialNumberUtils.isEmergency(aCall.number));
call.isSwitchable = pick(aCall.isSwitchable, true);
call.isMergeable = pick(aCall.isMergeable, true);
call.name = pick(aCall.name, "");
call.numberPresentaation = pick(aCall.numberPresentation, nsITelephonyService.CALL_PRESENTATION_ALLOWED);
call.namePresentaation = pick(aCall.namePresentation, nsITelephonyService.CALL_PRESENTATION_ALLOWED);
this._currentCalls[aClientId][aCall.callIndex] = call;
}
// Handle cached dial request.
if (this._cachedDialRequest && !this._getOneActiveCall(aClientId)) {
if (DEBUG) debug("All calls held. Perform the cached dial request.");
let request = this._cachedDialRequest;
this._sendDialCallRequest(request.clientId, request.options, request.callback);
this._cachedDialRequest = null;
}
let callInfo = new TelephonyCallInfo(call);
let callInfo = new TelephonyCallInfo(aCall);
this._notifyAllListeners("callStateChanged", [callInfo]);
},
@@ -1284,7 +1539,7 @@ TelephonyService.prototype = {
if (call) {
// TODO: Bug 977503 - B2G RIL: [CDMA] update callNumber when a waiting
// call comes after a 3way call.
this.notifyCallDisconnected(aClientId, call);
this._handleCallDisconnected(aClientId, call);
}
this._cdmaCallWaitingNumber = aCall.number;
@@ -1296,17 +1551,37 @@ TelephonyService.prototype = {
aCall.namePresentation]);
},
notifySupplementaryService: function(aClientId, aCallIndex, aNotification) {
notifySupplementaryService: function(aClientId, aNumber, aNotification) {
let notification = this._convertRILSuppSvcNotification(aNotification);
// Get the target call object for this notification.
let callIndex = -1;
let indexes = Object.keys(this.currentCalls);
if (indexes.length === 1) {
// Only one call exists. This should be the target.
callIndex = indexes[0];
} else {
// Find the call in |currentCalls| by the given number.
if (aNumber) {
for (let i in this._currentCalls) {
let call = this._currentCalls[aClientId][i];
if (call.number === aNumber) {
callIndex = i;
break;
}
}
}
}
this._notifyAllListeners("supplementaryServiceNotification",
[aClientId, aCallIndex, notification]);
[aClientId, callIndex, notification]);
},
notifyConferenceCallStateChanged: function(aState) {
_handleConferenceCallStateChanged: function(aState) {
if (DEBUG) debug("handleConferenceCallStateChanged: " + aState);
this._currentConferenceState = this._convertRILCallState(aState);
this._notifyAllListeners("conferenceCallStateChanged",
[this._currentConferenceState]);
this._currentConferenceState = aState;
this._notifyAllListeners("conferenceCallStateChanged", [aState]);
},
notifyUssdReceived: function(aClientId, aMessage, aSessionEnded) {
+3 -10
View File
@@ -10,25 +10,18 @@
"@mozilla.org/telephony/gonktelephonyservice;1"
%}
[scriptable, uuid(eab4b7b4-bf78-4c44-8182-ca305e70f971)]
[scriptable, uuid(d287e11a-0a65-4456-b481-c63d62afdb5d)]
interface nsIGonkTelephonyService : nsITelephonyService
{
void notifyAudioStateChanged(in unsigned long clientId, in short state);
void notifyCallDisconnected(in unsigned long clientId, in jsval call);
void notifyCallRing();
void notifyCallStateChanged(in unsigned long clientId, in jsval call,
[optional] in boolean skipStateConversion);
void notifyCurrentCalls(in unsigned long clientId, in jsval calls);
void notifyCdmaCallWaiting(in unsigned long clientId, in jsval waitingCall);
void notifySupplementaryService(in unsigned long clientId, in long callIndex,
void notifySupplementaryService(in unsigned long clientId, in AString number,
in AString notification);
void notifyConferenceCallStateChanged(in short state);
void notifyUssdReceived(in unsigned long clientId, in DOMString message,
in boolean sessionEnded);
};
@@ -25,6 +25,7 @@ qemu = true
[test_incall_mmi_call_hold.js]
[test_incall_mmi_call_waiting.js]
[test_incall_mmi_conference.js]
[test_incall_mmi_imei.js]
[test_incoming_already_connected.js]
[test_incoming_already_held.js]
[test_incoming_answer_hangup_oncallschanged.js]
@@ -0,0 +1,38 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const number = "0900000001";
let outCall;
function getIMEI() {
log("Test *#06# ...");
return gSendMMI("*#06#").then(aResult => {
ok(aResult.success, "success");
is(aResult.serviceCode, "scImei", "Service code IMEI");
// IMEI is hardcoded as "000000000000000".
// See it here {B2G_HOME}/external/qemu/telephony/android_modem.c
// (The aResult of +CGSN).
is(aResult.statusMessage, "000000000000000", "Emulator IMEI");
is(aResult.additionalInformation, undefined, "No additional information");
});
}
function testInCallMMI_IMEI() {
log('= testInCallMMI_IMEI =');
return gDial(number)
.then(call => outCall = call)
.then(() => gRemoteAnswer(outCall))
.then(() => getIMEI())
.then(() => gRemoteHangUpCalls([outCall]));
}
startTest(function() {
testInCallMMI_IMEI()
.catch(error => ok(false, "Promise reject: " + error))
.then(finish);
});
@@ -16,7 +16,7 @@ function testDialOutInvalidNumber() {
return telephony.dial(number).then(call => {
outCall = call;
ok(outCall);
is(outCall.id.number, number);
is(outCall.id.number, ""); // Emulator returns empty number for this call.
is(outCall.state, "dialing");
is(outCall, telephony.active);
@@ -65,6 +65,7 @@ startTestWithPermissions(['mobileconnection'], function() {
.then(() => testDial_EmergencyNumber())
.then(() => testDialEmergency_NormalNumber())
.then(() => testDialEmergency_EmergencyNumber())
.catch(error => ok(false, "Promise reject: " + error))
.then(() => setRadioEnabledAll(true))
.catch(error => ok(false, "Promise reject: " + error))
.then(finish);
+2 -54
View File
@@ -30,44 +30,6 @@ add_test(function test_parseMMI_undefined() {
run_next_test();
});
add_test(function test_parseMMI_one_digit_short_code() {
let mmi = parseMMI("1");
equal(mmi.fullMMI, "1");
equal(mmi.procedure, undefined);
equal(mmi.serviceCode, undefined);
equal(mmi.sia, undefined);
equal(mmi.sib, undefined);
equal(mmi.sic, undefined);
equal(mmi.pwd, undefined);
equal(mmi.dialNumber, undefined);
run_next_test();
});
add_test(function test_parseMMI_invalid_short_code() {
let mmi = parseMMI("11");
equal(mmi, null);
run_next_test();
});
add_test(function test_parseMMI_short_code() {
let mmi = parseMMI("21");
equal(mmi.fullMMI, "21");
equal(mmi.procedure, undefined);
equal(mmi.serviceCode, undefined);
equal(mmi.sia, undefined);
equal(mmi.sib, undefined);
equal(mmi.sic, undefined);
equal(mmi.pwd, undefined);
equal(mmi.dialNumber, undefined);
run_next_test();
});
add_test(function test_parseMMI_dial_string() {
let mmi = parseMMI("12345");
@@ -79,14 +41,7 @@ add_test(function test_parseMMI_dial_string() {
add_test(function test_parseMMI_USSD_without_asterisk_prefix() {
let mmi = parseMMI("123#");
equal(mmi.fullMMI, "123#");
equal(mmi.procedure, undefined);
equal(mmi.serviceCode, undefined);
equal(mmi.sia, undefined);
equal(mmi.sib, undefined);
equal(mmi.sic, undefined);
equal(mmi.pwd, undefined);
equal(mmi.dialNumber, undefined);
equal(mmi, null);
run_next_test();
});
@@ -109,14 +64,7 @@ add_test(function test_parseMMI_USSD() {
add_test(function test_parseMMI_USSD_with_two_sharps() {
let mmi = parseMMI("*225#4384903113430962#");
equal(mmi.fullMMI, "*225#4384903113430962#");
equal(mmi.procedure, undefined);
equal(mmi.serviceCode, undefined);
equal(mmi.sia, undefined);
equal(mmi.sib, undefined);
equal(mmi.sic, undefined);
equal(mmi.pwd, undefined);
equal(mmi.dialNumber, undefined);
equal(mmi, null);
run_next_test();
});
+5 -4
View File
@@ -37,9 +37,9 @@ XPCOMUtils.defineLazyGetter(this, "CP", function () {
XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
"@mozilla.org/system-message-internal;1",
"nsISystemMessagesInternal");
XPCOMUtils.defineLazyServiceGetter(this, "gRIL",
"@mozilla.org/ril;1",
"nsIRadioInterfaceLayer");
XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
"@mozilla.org/icc/iccservice;1",
"nsIIccService");
/**
* Helpers for WAP PDU processing.
@@ -104,7 +104,8 @@ this.WapPushManager = {
let mac = params && params.mac;
authInfo = CP.Authenticator.check(data.array.subarray(data.offset),
sec, mac, function getNetworkPin() {
let imsi = gRIL.getRadioInterface(options.serviceId).rilContext.imsi;
let icc = gIccService.getIccByServiceId(options.serviceId);
let imsi = icc ? icc.imsi : null;
return CP.Authenticator.formatImsi(imsi);
});
}
+55
View File
@@ -75,6 +75,45 @@ dictionary MobileMessageFilter
[EnforceRange] unsigned long long? threadId = 0;
};
/**
* TON defined in |Table 10.5.118: Called party BCD number| of 3GPP TS 24.008.
* It's used in SM-RL originator / destination address element as defined in
* |8.2.5.2 Destination address element| of 3GPP TS 24.011.
*/
enum TypeOfNumber { "unknown", "international", "national", "network-specific",
"dedicated-access-short-code" };
/**
* NPI defined in |Table 10.5.118: Called party BCD number| of 3GPP TS 24.008.
* It's used in SM-RL originator / destination address element as defined in
* |8.2.5.2 Destination address element| of 3GPP TS 24.011.
*/
enum NumberPlanIdentification { "unknown", "isdn", "data", "telex", "national",
"private" };
/**
* Type of address used in SmscAddress.
*
* As described in |3.1 Parameters Definitions| of 3GPP TS 27.005, the default
* value of <tosca> should be 129 (typeOfNumber=unknown,
* numberPlanIdentification=isdn) if the number does not begin with '+'.
*
* |setSmscAddress| updates typeOfNumber to international automatically if the
* given number begins with '+'.
*/
dictionary TypeOfAddress {
TypeOfNumber typeOfNumber = "unknown";
NumberPlanIdentification numberPlanIdentification = "isdn";
};
/**
* SMSC address.
*/
dictionary SmscAddress {
DOMString address;
TypeOfAddress typeOfAddress;
};
[Pref="dom.sms.enabled",
CheckAnyPermissions="sms",
AvailableIn="CertifiedApps"]
@@ -157,6 +196,22 @@ interface MozMobileMessageManager : EventTarget
[Throws]
DOMRequest getSmscAddress(optional unsigned long serviceId);
/**
* Set the SMSC address.
*
* @param smscAddress
* SMSC address to use.
* Reject if smscAddress.address does not present.
* @param serviceId (optional)
* The ID of the RIL service which needs to be specified under
* the multi-sim scenario.
* @return a Promise
* Resolve if success. Otherwise, reject with error cause.
*/
[NewObject]
Promise<void> setSmscAddress(optional SmscAddress smscAddress,
optional unsigned long serviceId);
attribute EventHandler onreceived;
attribute EventHandler onretrieving;
attribute EventHandler onsending;
+20 -10
View File
@@ -171,13 +171,13 @@ this.MobileIdentityManager = {
this._iccInfo = [];
for (let i = 0; i < this.ril.numRadioInterfaces; i++) {
let rilContext = this.ril.getRadioInterface(i).rilContext;
if (!rilContext) {
log.warn("Tried to get the RIL context for an invalid service ID " + i);
let icc = this.iccService.getIccByServiceId(i);
if (!icc) {
log.warn("Tried to get the Icc instance for an invalid service ID " + i);
continue;
}
let info = rilContext.iccInfo;
let info = icc.iccInfo;
if (!info || !info.iccid ||
!info.mcc || !info.mcc.length ||
!info.mnc || !info.mnc.length) {
@@ -185,6 +185,20 @@ this.MobileIdentityManager = {
continue;
}
// GSM SIMs may have MSISDN while CDMA SIMs may have MDN
let phoneNumber = null;
try {
if (info.iccType === "sim" || info.iccType === "usim") {
let gsmInfo = info.QueryInterface(Ci.nsIGsmIccInfo);
phoneNumber = gsmInfo.msisdn;
} else if (info.iccType === "ruim" || info.iccType === "csim") {
let cdmaInfo = info.QueryInterface(Ci.nsICdmaIccInfo);
phoneNumber = cdmaInfo.mdn;
}
} catch (e) {
log.error("Failed to retrieve phoneNumber: " + e);
}
let connection = this.mobileConnectionService.getItemByServiceId(i);
let voice = connection && connection.voice;
let data = connection && connection.data;
@@ -208,18 +222,14 @@ this.MobileIdentityManager = {
iccId: info.iccid,
mcc: info.mcc,
mnc: info.mnc,
// GSM SIMs may have MSISDN while CDMA SIMs may have MDN
msisdn: info.msisdn || info.mdn || null,
msisdn: phoneNumber,
operator: operator,
roaming: voice && voice.roaming
});
// We need to subscribe to ICC change notifications so we can refresh
// the cache if any change is observed.
let icc = this.iccService.getIccByServiceId(i);
if (icc) {
icc.registerListener(iccListener);
}
icc.registerListener(iccListener);
}
return this._iccInfo;
+30 -27
View File
@@ -46,16 +46,37 @@ const MCC = "aMcc";
const ANOTHER_MCC = "anotherMcc";
const OPERATOR = "aOperator";
const ANOTHER_OPERATOR = "anotherOperator";
const ICC_INFO = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
Ci.nsIIccInfo]),
iccType: "usim",
iccid: ICC_ID,
mcc: MCC,
mnc: MNC,
msisdn: PHONE_NUMBER,
operator: OPERATOR
};
const ANOTHER_ICC_INFO = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
Ci.nsIIccInfo]),
iccType: "usim",
iccid: ANOTHER_ICC_ID,
mcc: ANOTHER_MCC,
mnc: ANOTHER_MNC,
msisdn: ANOTHER_PHONE_NUMBER,
operator: ANOTHER_OPERATOR
};
const INVALID_ICC_INFO = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
Ci.nsIIccInfo]),
iccType: "usim",
iccid: null,
mcc: "",
mnc: "",
msisdn: "",
operator: ""
};
const RADIO_INTERFACE = {
rilContext: {
iccInfo: {
iccid: ICC_ID,
mcc: MCC,
mnc: MNC,
msisdn: PHONE_NUMBER,
operator: OPERATOR
}
},
voice: {
network: {
shortName: OPERATOR
@@ -69,15 +90,6 @@ const RADIO_INTERFACE = {
}
};
const ANOTHER_RADIO_INTERFACE = {
rilContext: {
iccInfo: {
iccid: ANOTHER_ICC_ID,
mcc: ANOTHER_MCC,
mnc: ANOTHER_MNC,
msisdn: ANOTHER_PHONE_NUMBER,
operator: ANOTHER_OPERATOR
}
},
voice: {
network: {
shortName: ANOTHER_OPERATOR
@@ -92,15 +104,6 @@ const ANOTHER_RADIO_INTERFACE = {
};
const INVALID_RADIO_INTERFACE = {
rilContext: {
iccInfo: {
iccid: null,
mcc: "",
mnc: "",
msisdn: "",
operator: ""
}
},
voice: {
network: {
shortName: ""
@@ -1003,33 +1003,35 @@ add_test(function() {
};
MobileIdentityManager._iccService = {
_iccs: [],
_listeners: [],
_iccInfos: [ICC_INFO, ANOTHER_ICC_INFO],
getIccByServiceId: function(aClientId) {
let self = this;
this_iccs.push({
return {
get iccInfo() {
return self._iccInfos[aClientId];
},
registerListener: function(aIccListener) {
self._listeners.push(aIccListener);
},
unregisterListener: function() {
self._listeners.pop();
}
});
};
}
};
let ui = new MockUi();
ui.startFlow = function() {
// At this point we've already built the ICC cache.
let interfaces = MobileIdentityManager._ril._interfaces;
for (let i = 0; i < interfaces.length; i++) {
let interfaceIccInfo = interfaces[i].rilContext.iccInfo;
let mockIccInfo = [ICC_INFO, ANOTHER_ICC_INFO];
for (let i = 0; i < mockIccInfo.length; i++) {
let mIdIccInfo = MobileIdentityManager._iccInfo[i];
do_check_eq(interfaceIccInfo.iccid, mIdIccInfo.iccId);
do_check_eq(interfaceIccInfo.mcc, mIdIccInfo.mcc);
do_check_eq(interfaceIccInfo.mnc, mIdIccInfo.mnc);
do_check_eq(interfaceIccInfo.msisdn, mIdIccInfo.msisdn);
do_check_eq(interfaceIccInfo.operator, mIdIccInfo.operator);
do_check_eq(mockIccInfo[i].iccid, mIdIccInfo.iccId);
do_check_eq(mockIccInfo[i].mcc, mIdIccInfo.mcc);
do_check_eq(mockIccInfo[i].mnc, mIdIccInfo.mnc);
do_check_eq(mockIccInfo[i].msisdn, mIdIccInfo.msisdn);
do_check_eq(mockIccInfo[i].operator, mIdIccInfo.operator);
}
// We should have listeners for each valid icc.
@@ -1111,18 +1113,21 @@ add_test(function() {
};
MobileIdentityManager._iccService = {
_iccs: [],
_listeners: [],
_iccInfos: [INVALID_ICC_INFO],
getIccByServiceId: function(aClientId) {
let self = this;
this_iccs.push({
return {
get iccInfo() {
return self._iccInfos[aClientId];
},
registerListener: function(aIccListener) {
self._listeners.push(aIccListener);
},
unregisterListener: function() {
self._listeners.pop();
}
});
};
}
};