Bug 1123580 - Reorganize mmi parsing related code. r=hsinyi

This commit is contained in:
Szu-Yu Chen [:aknow] 2015-01-23 18:34:13 +08:00
parent 84c5c2d1fc
commit 48960dace8
4 changed files with 144 additions and 174 deletions

View File

@ -0,0 +1,128 @@
/* 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";
this.EXPORTED_SYMBOLS = ["DialNumberUtils"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/systemlibs.js");
const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
const MMI_MATCH_GROUP_FULL_MMI = 1;
const MMI_MATCH_GROUP_PROCEDURE = 2;
const MMI_MATCH_GROUP_SERVICE_CODE = 3;
const MMI_MATCH_GROUP_SIA = 4;
const MMI_MATCH_GROUP_SIB = 5;
const MMI_MATCH_GROUP_SIC = 6;
const MMI_MATCH_GROUP_PWD_CONFIRM = 7;
const MMI_MATCH_GROUP_DIALING_NUMBER = 8;
this.DialNumberUtils = {
/**
* Check a given number against the list of emergency numbers provided by the
* RIL.
*/
isEmergency: function(aNumber) {
// Check ril provided numbers first.
let numbers = libcutils.property_get("ril.ecclist") ||
libcutils.property_get("ro.ril.ecclist");
if (numbers) {
numbers = numbers.split(",");
} else {
// No ecclist system property, so use our own list.
numbers = DEFAULT_EMERGENCY_NUMBERS;
}
return numbers.indexOf(aNumber) != -1;
},
_mmiRegExp: (function() {
// Procedure, which could be *, #, *#, **, ##
let procedure = "(\\*[*#]?|##?)";
// Service code, which is a 2 or 3 digits that uniquely specifies the
// Supplementary Service associated with the MMI code.
let serviceCode = "(\\d{2,3})";
// Supplementary Information SIA, SIB and SIC.
// Where a particular service request does not require any SI, "*SI" is
// not entered. The use of SIA, SIB and SIC is optional and shall be
// entered in any of the following formats:
// - *SIA*SIB*SIC#
// - *SIA*SIB#
// - *SIA**SIC#
// - *SIA#
// - **SIB*SIC#
// - ***SIC#
//
// Also catch the additional NEW_PASSWORD for the case of a password
// registration procedure. Ex:
// - * 03 * ZZ * OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
// - ** 03 * ZZ * OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
// - * 03 ** OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
// - ** 03 ** OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
let si = "\\*([^*#]*)";
let allSi = "";
for (let i = 0; i < 4; ++i) {
allSi = "(?:" + si + allSi + ")?";
}
let fullmmi = "(" + procedure + serviceCode + allSi + "#)";
// Dial string after the #.
let optionalDialString = "([^#]+)?";
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.
*
* An MMI code should be:
* - Activation (*SC*SI#).
* - Deactivation (#SC*SI#).
* - Interrogation (*#SC*SI#).
* - Registration (**SC*SI#).
* - Erasure (##SC*SI#).
* where SC = Service Code (2 or 3 digits) and SI = Supplementary Info
* (variable length).
*/
parseMMI: function(aString) {
let matches = this._mmiRegExp.exec(aString);
if (matches) {
return {
fullMMI: matches[MMI_MATCH_GROUP_FULL_MMI],
procedure: matches[MMI_MATCH_GROUP_PROCEDURE],
serviceCode: matches[MMI_MATCH_GROUP_SERVICE_CODE],
sia: matches[MMI_MATCH_GROUP_SIA],
sib: matches[MMI_MATCH_GROUP_SIB],
sic: matches[MMI_MATCH_GROUP_SIC],
pwd: matches[MMI_MATCH_GROUP_PWD_CONFIRM],
dialNumber: matches[MMI_MATCH_GROUP_DIALING_NUMBER]
};
}
if (this._isPoundString(aString) || this._isShortString(aString)) {
return {
fullMMI: aString
};
}
return null;
}
};

View File

@ -10,7 +10,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/systemlibs.js");
XPCOMUtils.defineLazyGetter(this, "RIL", function () {
let obj = {};
@ -48,20 +47,9 @@ const DIAL_ERROR_INVALID_STATE_ERROR = "InvalidStateError";
const DIAL_ERROR_OTHER_CONNECTION_IN_USE = "OtherConnectionInUse";
const DIAL_ERROR_BAD_NUMBER = RIL.GECKO_CALL_ERROR_BAD_NUMBER;
const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
const TONES_GAP_DURATION = 70;
// MMI match groups
const MMI_MATCH_GROUP_FULL_MMI = 1;
const MMI_MATCH_GROUP_PROCEDURE = 2;
const MMI_MATCH_GROUP_SERVICE_CODE = 3;
const MMI_MATCH_GROUP_SIA = 4;
const MMI_MATCH_GROUP_SIB = 5;
const MMI_MATCH_GROUP_SIC = 6;
const MMI_MATCH_GROUP_PWD_CONFIRM = 7;
const MMI_MATCH_GROUP_DIALING_NUMBER = 8;
let DEBUG;
function debug(s) {
dump("TelephonyService: " + s + "\n");
@ -93,6 +81,12 @@ XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function() {
return ns.PhoneNumberUtils;
});
XPCOMUtils.defineLazyGetter(this, "gDialNumberUtils", function() {
let ns = {};
Cu.import("resource://gre/modules/DialNumberUtils.jsm", ns);
return ns.DialNumberUtils;
});
function MobileCallForwardingOptions(aOptions) {
for (let key in aOptions) {
this[key] = aOptions[key];
@ -160,8 +154,6 @@ function TelephonyService() {
this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
this._listeners = [];
this._mmiRegExp = null;
this._isDialing = false;
this._cachedDialRequest = null;
this._currentCalls = {};
@ -343,26 +335,6 @@ TelephonyService.prototype = {
});
},
/**
* Check a given number against the list of emergency numbers provided by the
* RIL.
*
* @param aNumber
* The number to look up.
*/
_isEmergencyNumber: function(aNumber) {
// Check ril provided numbers first.
let numbers = libcutils.property_get("ril.ecclist") ||
libcutils.property_get("ro.ril.ecclist");
if (numbers) {
numbers = numbers.split(",");
} else {
// No ecclist system property, so use our own list.
numbers = DEFAULT_EMERGENCY_NUMBERS;
}
return numbers.indexOf(aNumber) != -1;
},
/**
* Checks whether to temporarily suppress caller id for the call.
*
@ -553,7 +525,7 @@ TelephonyService.prototype = {
isDialEmergency: aIsDialEmergency }, aCallback);
}
} else {
let mmi = this._parseMMI(aNumber);
let mmi = gDialNumberUtils.parseMMI(aNumber);
if (!mmi) {
this._dialCall(aClientId,
{ number: aNumber,
@ -602,7 +574,7 @@ TelephonyService.prototype = {
return;
}
aOptions.isEmergency = this._isEmergencyNumber(aOptions.number);
aOptions.isEmergency = gDialNumberUtils.isEmergency(aOptions.number);
if (aOptions.isEmergency) {
// Automatically select a proper clientId for emergency call.
aClientId = gRadioInterfaceLayer.getClientIdForEmergencyCall() ;
@ -750,138 +722,6 @@ TelephonyService.prototype = {
});
},
/**
* Build the regex to parse MMI string. TS.22.030
*
* The resulting groups after matching will be:
* 1 = full MMI string that might be used as a USSD request.
* 2 = MMI procedure.
* 3 = Service code.
* 4 = SIA.
* 5 = SIB.
* 6 = SIC.
* 7 = Password registration.
* 8 = Dialing number.
*/
_buildMMIRegExp: function() {
// The general structure of the codes is as follows:
// - Activation (*SC*SI#).
// - Deactivation (#SC*SI#).
// - Interrogation (*#SC*SI#).
// - Registration (**SC*SI#).
// - Erasure (##SC*SI#).
//
// where SC = Service Code (2 or 3 digits) and SI = Supplementary Info
// (variable length).
// Procedure, which could be *, #, *#, **, ##
let procedure = "(\\*[*#]?|##?)";
// Service code, which is a 2 or 3 digits that uniquely specifies the
// Supplementary Service associated with the MMI code.
let serviceCode = "(\\d{2,3})";
// Supplementary Information SIA, SIB and SIC. SIA may comprise e.g. a PIN
// code or Directory Number, SIB may be used to specify the tele or bearer
// service and SIC to specify the value of the "No Reply Condition Timer".
// Where a particular service request does not require any SI, "*SI" is
// not entered. The use of SIA, SIB and SIC is optional and shall be
// entered in any of the following formats:
// - *SIA*SIB*SIC#
// - *SIA*SIB#
// - *SIA**SIC#
// - *SIA#
// - **SIB*SIC#
// - ***SIC#
//
// Also catch the additional NEW_PASSWORD for the case of a password
// registration procedure. Ex:
// - * 03 * ZZ * OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
// - ** 03 * ZZ * OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
// - * 03 ** OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
// - ** 03 ** OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD #
let si = "\\*([^*#]*)";
let allSi = "";
for (let i = 0; i < 4; ++i) {
allSi = "(?:" + si + allSi + ")?";
}
let fullmmi = "(" + procedure + serviceCode + allSi + "#)";
// Dial string after the #.
let optionalDialString = "([^#]+)?";
return new RegExp("^" + fullmmi + optionalDialString + "$");
},
/**
* Provide the regex to parse MMI string.
*/
_getMMIRegExp: function() {
if (!this._mmiRegExp) {
this._mmiRegExp = this._buildMMIRegExp();
}
return this._mmiRegExp;
},
/**
* Helper to parse # string. TS.22.030 Figure 3.5.3.2.
*/
_isPoundString: function(aMmiString) {
return (aMmiString.charAt(aMmiString.length - 1) === "#");
},
/**
* Helper to parse short string. TS.22.030 Figure 3.5.3.2.
*/
_isShortString: function(aMmiString) {
if (aMmiString.length > 2) {
return false;
}
// Input string is
// - emergency number or
// - 2 digits starting with a "1"
if (this._isEmergencyNumber(aMmiString) ||
(aMmiString.length == 2) && (aMmiString.charAt(0) === '1')) {
return false;
}
return true;
},
/**
* Helper to parse MMI/USSD string. TS.22.030 Figure 3.5.3.2.
*/
_parseMMI: function(aMmiString) {
if (!aMmiString) {
return null;
}
let matches = this._getMMIRegExp().exec(aMmiString);
if (matches) {
return {
fullMMI: matches[MMI_MATCH_GROUP_FULL_MMI],
procedure: matches[MMI_MATCH_GROUP_PROCEDURE],
serviceCode: matches[MMI_MATCH_GROUP_SERVICE_CODE],
sia: matches[MMI_MATCH_GROUP_SIA],
sib: matches[MMI_MATCH_GROUP_SIB],
sic: matches[MMI_MATCH_GROUP_SIC],
pwd: matches[MMI_MATCH_GROUP_PWD_CONFIRM],
dialNumber: matches[MMI_MATCH_GROUP_DIALING_NUMBER]
};
}
if (this._isPoundString(aMmiString) || this._isShortString(aMmiString)) {
return {
fullMMI: aMmiString
};
}
return null;
},
_serviceCodeToKeyString: function(aServiceCode) {
switch (aServiceCode) {
case RIL.MMI_SC_CFU:
@ -1178,7 +1018,7 @@ TelephonyService.prototype = {
aCall.clientId = aClientId;
aCall.state = nsITelephonyService.CALL_STATE_DISCONNECTED;
aCall.isEmergency = this._isEmergencyNumber(aCall.number);
aCall.isEmergency = gDialNumberUtils.isEmergency(aCall.number);
let duration = ("started" in aCall && typeof aCall.started == "number") ?
new Date().getTime() - aCall.started : 0;
@ -1273,12 +1113,12 @@ TelephonyService.prototype = {
call.state = aCall.state;
call.number = aCall.number;
call.isConference = aCall.isConference;
call.isEmergency = this._isEmergencyNumber(aCall.number);
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, this._isEmergencyNumber(aCall.number));
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, "");

View File

@ -66,6 +66,9 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
'gonk/TelephonyService.js',
'gonk/TelephonyService.manifest',
]
EXTRA_JS_MODULES += [
'gonk/DialNumberUtils.jsm'
]
FAIL_ON_WARNINGS = True
include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -4,15 +4,14 @@
subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this);
let NS = {};
subscriptLoader.loadSubScript("resource://gre/components/TelephonyService.js",
NS);
subscriptLoader.loadSubScript("resource://gre/modules/DialNumberUtils.jsm", NS);
function run_test() {
run_next_test();
}
function parseMMI(mmiString) {
return NS.TelephonyService.prototype._parseMMI(mmiString);
return NS.DialNumberUtils.parseMMI(mmiString);
}
add_test(function test_parseMMI_empty() {