mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 889737 - Part 09: Dial MMI (ril). r=vicamo
This commit is contained in:
parent
bbd007d9bd
commit
0488a241d4
@ -25,6 +25,8 @@ const MOBILENETWORKINFO_CID =
|
||||
Components.ID("{a6c8416c-09b4-46d1-bf29-6520d677d085}");
|
||||
const MOBILECELLINFO_CID =
|
||||
Components.ID("{0635d9ab-997e-4cdf-84e7-c1883752dff3}");
|
||||
const TELEPHONYCALLBACK_CID =
|
||||
Components.ID("{6e1af17e-37f3-11e4-aed3-60a44c237d2b}");
|
||||
|
||||
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
|
||||
const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
|
||||
@ -44,6 +46,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
|
||||
"@mozilla.org/ril;1",
|
||||
"nsIRadioInterfaceLayer");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gGonkTelephonyService",
|
||||
"@mozilla.org/telephony/telephonyservice;1",
|
||||
"nsIGonkTelephonyService");
|
||||
|
||||
let DEBUG = RIL.DEBUG_RIL;
|
||||
function debug(s) {
|
||||
dump("MobileConnectionService: " + s + "\n");
|
||||
@ -134,6 +140,41 @@ MMIResult.prototype = {
|
||||
additionalInformation: 'r'},
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrap a MobileConnectionCallback to a TelephonyCallback.
|
||||
*/
|
||||
function TelephonyCallback(aCallback) {
|
||||
this.callback = aCallback;
|
||||
}
|
||||
TelephonyCallback.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyCallback]),
|
||||
classID: TELEPHONYCALLBACK_CID,
|
||||
|
||||
notifyDialMMI: function(mmiServiceCode) {
|
||||
this.serviceCode = mmiServiceCode;
|
||||
},
|
||||
|
||||
notifyDialMMISuccess: function(result) {
|
||||
this.callback.notifySendCancelMmiSuccess(result);
|
||||
},
|
||||
|
||||
notifyDialMMIError: function(error) {
|
||||
this.callback.notifyError(error, "", this.serviceCode);
|
||||
},
|
||||
|
||||
notifyDialMMIErrorWithInfo: function(error, info) {
|
||||
this.callback.notifyError(error, "", this.serviceCode, info);
|
||||
},
|
||||
|
||||
notifyDialError: function() {
|
||||
throw Cr.NS_ERROR_UNEXPECTED;
|
||||
},
|
||||
|
||||
notifyDialSuccess: function() {
|
||||
throw Cr.NS_ERROR_UNEXPECTED;
|
||||
},
|
||||
};
|
||||
|
||||
function MobileConnectionProvider(aClientId, aRadioInterface) {
|
||||
this._clientId = aClientId;
|
||||
this._radioInterface = aRadioInterface;
|
||||
@ -188,7 +229,7 @@ MobileConnectionProvider.prototype = {
|
||||
key = "ro.telephony.default_network";
|
||||
let indexString = libcutils.property_get(key, "");
|
||||
let index = parseInt(indexString, 10);
|
||||
if (DEBUG) this._debug("Fallback to " + key + ": " + index)
|
||||
if (DEBUG) this._debug("Fallback to " + key + ": " + index);
|
||||
|
||||
let networkTypes = RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO[index];
|
||||
supportedNetworkTypes = networkTypes ?
|
||||
@ -382,10 +423,7 @@ MobileConnectionProvider.prototype = {
|
||||
},
|
||||
|
||||
_rulesToCallForwardingOptions: function(aRules) {
|
||||
for (let i = 0; i < aRules.length; i++) {
|
||||
let info = new CallForwardingOptions(aRules[i]);
|
||||
aRules[i] = info;
|
||||
}
|
||||
return aRules.map(rule => new CallForwardingOptions(rule));
|
||||
},
|
||||
|
||||
_dispatchNotifyError: function(aCallback, aErrorMsg) {
|
||||
@ -515,6 +553,13 @@ MobileConnectionProvider.prototype = {
|
||||
this.deliverListenerEvent("notifyRadioStateChanged");
|
||||
},
|
||||
|
||||
notifyCFStateChanged: function(aAction, aReason, aNumber, aTimeSeconds,
|
||||
aServiceClass) {
|
||||
this.deliverListenerEvent("notifyCFStateChanged",
|
||||
[true, aAction, aReason, aNumber, aTimeSeconds,
|
||||
aServiceClass]);
|
||||
},
|
||||
|
||||
getSupportedNetworkTypes: function(aTypes) {
|
||||
aTypes.value = this.supportedNetworkTypes.slice();
|
||||
return aTypes.value.length;
|
||||
@ -680,49 +725,8 @@ MobileConnectionProvider.prototype = {
|
||||
},
|
||||
|
||||
sendMMI: function(aMmi, aCallback) {
|
||||
this._radioInterface.sendWorkerMessage("sendMMI", {mmi: aMmi},
|
||||
(function(aResponse) {
|
||||
aResponse.serviceCode = aResponse.mmiServiceCode || "";
|
||||
// We expect to have an IMEI at this point if the request was supposed
|
||||
// to query for the IMEI, so getting a successful reply from the RIL
|
||||
// without containing an actual IMEI number is considered an error.
|
||||
if (aResponse.serviceCode === RIL.MMI_KS_SC_IMEI &&
|
||||
!aResponse.statusMessage) {
|
||||
aResponse.errorMsg = aResponse.errorMsg ||
|
||||
RIL.GECKO_ERROR_GENERIC_FAILURE;
|
||||
}
|
||||
|
||||
if (aResponse.errorMsg) {
|
||||
if (aResponse.additionalInformation) {
|
||||
aCallback.notifyError(aResponse.errorMsg, "",
|
||||
aResponse.serviceCode,
|
||||
aResponse.additionalInformation);
|
||||
} else {
|
||||
aCallback.notifyError(aResponse.errorMsg, "",
|
||||
aResponse.serviceCode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aResponse.isSetCallForward) {
|
||||
this.deliverListenerEvent("notifyCFStateChanged",
|
||||
[!aResponse.errorMsg, aResponse.action,
|
||||
aResponse.reason, aResponse.number,
|
||||
aResponse.timeSeconds, aResponse.serviceClass]);
|
||||
}
|
||||
|
||||
// MMI query call forwarding options request returns a set of rules that
|
||||
// will be exposed in the form of an array of MozCallForwardingOptions
|
||||
// instances.
|
||||
if (aResponse.serviceCode === RIL.MMI_KS_SC_CALL_FORWARDING &&
|
||||
aResponse.additionalInformation) {
|
||||
this._rulesToCallForwardingOptions(aResponse.additionalInformation);
|
||||
}
|
||||
|
||||
let mmiResult = new MMIResult(aResponse);
|
||||
aCallback.notifySendCancelMmiSuccess(mmiResult);
|
||||
return false;
|
||||
}).bind(this));
|
||||
let telephonyCallback = new TelephonyCallback(aCallback);
|
||||
gGonkTelephonyService.dialMMI(this._clientId, aMmi, telephonyCallback);
|
||||
},
|
||||
|
||||
cancelMMI: function(aCallback) {
|
||||
@ -762,11 +766,9 @@ MobileConnectionProvider.prototype = {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.deliverListenerEvent("notifyCFStateChanged",
|
||||
[!aResponse.errorMsg, aResponse.action,
|
||||
aResponse.reason, aResponse.number,
|
||||
aResponse.timeSeconds, aResponse.serviceClass]);
|
||||
|
||||
this.notifyCFStateChanged(aResponse.action, aResponse.reason,
|
||||
aResponse.number, aResponse.timeSeconds,
|
||||
aResponse.serviceClass);
|
||||
aCallback.notifySuccess();
|
||||
return false;
|
||||
}).bind(this));
|
||||
@ -786,9 +788,8 @@ MobileConnectionProvider.prototype = {
|
||||
return false;
|
||||
}
|
||||
|
||||
let infos = aResponse.rules;
|
||||
this._rulesToCallForwardingOptions(infos);
|
||||
aCallback.notifyGetCallForwardingSuccess(infos);
|
||||
aCallback.notifyGetCallForwardingSuccess(
|
||||
this._rulesToCallForwardingOptions(aResponse.rules));
|
||||
return false;
|
||||
}).bind(this));
|
||||
},
|
||||
@ -1216,6 +1217,17 @@ MobileConnectionService.prototype = {
|
||||
provider.deliverListenerEvent("notifyLastKnownHomeNetworkChanged");
|
||||
},
|
||||
|
||||
notifyCFStateChanged: function(aClientId, aAction, aReason, aNumber,
|
||||
aTimeSeconds, aServiceClass) {
|
||||
if (DEBUG) {
|
||||
debug("notifyCFStateChanged for " + aClientId);
|
||||
}
|
||||
|
||||
let provider = this.getItemByServiceId(aClientId);
|
||||
provider.notifyCFStateChanged(aAction, aReason, aNumber, aTimeSeconds,
|
||||
aServiceClass);
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIObserver interface.
|
||||
*/
|
||||
|
@ -9,7 +9,7 @@
|
||||
"@mozilla.org/mobileconnection/gonkmobileconnectionservice;1"
|
||||
%}
|
||||
|
||||
[scriptable, uuid(e54fa0a4-d357-48ef-9a1e-ffc9705b44b1)]
|
||||
[scriptable, uuid(b0310517-e7f6-4fa5-a52e-fa6ff35c8fc1)]
|
||||
interface nsIGonkMobileConnectionService : nsIMobileConnectionService
|
||||
{
|
||||
void notifyNetworkInfoChanged(in unsigned long clientId, in jsval networkInfo);
|
||||
@ -46,4 +46,11 @@ interface nsIGonkMobileConnectionService : nsIMobileConnectionService
|
||||
|
||||
void notifyLastHomeNetworkChanged(in unsigned long clientId,
|
||||
in DOMString network);
|
||||
|
||||
void notifyCFStateChanged(in unsigned long clientId,
|
||||
in unsigned short action,
|
||||
in unsigned short reason,
|
||||
in DOMString number,
|
||||
in unsigned short timeSeconds,
|
||||
in unsigned short serviceClass);
|
||||
};
|
||||
|
@ -33,7 +33,7 @@ function testInvalidMMICode() {
|
||||
ok(true, MMI_CODE + " fail");
|
||||
is(aError.name, "emMmiError", "MMI error name");
|
||||
is(aError.message, "", "No message");
|
||||
is(aError.serviceCode, "", "No serviceCode");
|
||||
is(aError.serviceCode, "scUssd", "Service code USSD");
|
||||
is(aError.additionalInformation, null, "No additional information");
|
||||
});
|
||||
}
|
||||
|
@ -57,20 +57,6 @@ const EMERGENCY_CB_MODE_TIMEOUT_MS = 300000; // 5 mins = 300000 ms.
|
||||
|
||||
const ICC_MAX_LINEAR_FIXED_RECORDS = 0xfe;
|
||||
|
||||
// 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;
|
||||
|
||||
const MMI_MAX_LENGTH_SHORT_CODE = 2;
|
||||
|
||||
const MMI_END_OF_USSD = "#";
|
||||
|
||||
const GET_CURRENT_CALLS_RETRY_MAX = 3;
|
||||
|
||||
let RILQUIRKS_CALLSTATE_EXTRA_UINT32;
|
||||
@ -2402,208 +2388,11 @@ RilObject.prototype = {
|
||||
{callback: callback});
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the dial number to extract its mmi code part.
|
||||
*
|
||||
* @param number
|
||||
* Phone number to be parsed
|
||||
*/
|
||||
parseMMIFromDialNumber: function(options) {
|
||||
// We don't have to parse mmi in cdma.
|
||||
if (!this._isCdma) {
|
||||
options.mmi = this._parseMMI(options.number);
|
||||
}
|
||||
this.sendChromeMessage(options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to parse MMI/USSD string. TS.22.030 Figure 3.5.3.2.
|
||||
*/
|
||||
_parseMMI: function(mmiString) {
|
||||
if (!mmiString || !mmiString.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let matches = this._getMMIRegExp().exec(mmiString);
|
||||
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(mmiString) || this._isMMIShortString(mmiString)) {
|
||||
return {
|
||||
fullMMI: mmiString
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Build the regex to parse MMI string.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @see TS.22.030 Figure 3.5.3.2.
|
||||
*/
|
||||
_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).
|
||||
|
||||
// MMI procedure, which could be *, #, *#, **, ##
|
||||
let procedure = "(\\*[*#]?|##?)";
|
||||
|
||||
// MMI 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})";
|
||||
|
||||
// MMI 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 dialString = "([^#]*)";
|
||||
|
||||
return new RegExp(fullmmi + dialString);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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(mmiString) {
|
||||
return (mmiString.charAt(mmiString.length - 1) === MMI_END_OF_USSD);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper to parse short string. TS.22.030 Figure 3.5.3.2.
|
||||
*/
|
||||
_isMMIShortString: function(mmiString) {
|
||||
if (mmiString.length > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Should take care of checking if the string is an emergency number
|
||||
// in Bug 889737. See Bug 1023141 for more background.
|
||||
|
||||
// In a call case.
|
||||
if (Object.getOwnPropertyNames(this.currentCalls).length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Input string is 2 digits starting with a "1"
|
||||
if ((mmiString.length == 2) && (mmiString.charAt(0) === '1')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_serviceCodeToKeyString: function(serviceCode) {
|
||||
switch (serviceCode) {
|
||||
case MMI_SC_CFU:
|
||||
case MMI_SC_CF_BUSY:
|
||||
case MMI_SC_CF_NO_REPLY:
|
||||
case MMI_SC_CF_NOT_REACHABLE:
|
||||
case MMI_SC_CF_ALL:
|
||||
case MMI_SC_CF_ALL_CONDITIONAL:
|
||||
return MMI_KS_SC_CALL_FORWARDING;
|
||||
case MMI_SC_PIN:
|
||||
return MMI_KS_SC_PIN;
|
||||
case MMI_SC_PIN2:
|
||||
return MMI_KS_SC_PIN2;
|
||||
case MMI_SC_PUK:
|
||||
return MMI_KS_SC_PUK;
|
||||
case MMI_SC_PUK2:
|
||||
return MMI_KS_SC_PUK2;
|
||||
case MMI_SC_IMEI:
|
||||
return MMI_KS_SC_IMEI;
|
||||
case MMI_SC_CLIP:
|
||||
return MMI_KS_SC_CLIP;
|
||||
case MMI_SC_CLIR:
|
||||
return MMI_KS_SC_CLIR;
|
||||
case MMI_SC_BAOC:
|
||||
case MMI_SC_BAOIC:
|
||||
case MMI_SC_BAOICxH:
|
||||
case MMI_SC_BAIC:
|
||||
case MMI_SC_BAICr:
|
||||
case MMI_SC_BA_ALL:
|
||||
case MMI_SC_BA_MO:
|
||||
case MMI_SC_BA_MT:
|
||||
return MMI_KS_SC_CALL_BARRING;
|
||||
case MMI_SC_CALL_WAITING:
|
||||
return MMI_KS_SC_CALL_WAITING;
|
||||
default:
|
||||
return MMI_KS_SC_USSD;
|
||||
}
|
||||
},
|
||||
|
||||
sendMMI: function(options) {
|
||||
if (DEBUG) {
|
||||
this.context.debug("SendMMI " + JSON.stringify(options));
|
||||
}
|
||||
|
||||
let mmi = this._parseMMI(options.mmi);
|
||||
if (DEBUG) {
|
||||
this.context.debug("MMI " + JSON.stringify(mmi));
|
||||
}
|
||||
|
||||
let _sendMMIError = (function(errorMsg) {
|
||||
options.success = false;
|
||||
options.errorMsg = errorMsg;
|
||||
@ -2611,14 +2400,12 @@ RilObject.prototype = {
|
||||
}).bind(this);
|
||||
|
||||
// It's neither a valid mmi code nor an ongoing ussd.
|
||||
let mmi = options.mmi;
|
||||
if (!mmi && !this._ussdSession) {
|
||||
_sendMMIError(MMI_ERROR_KS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
options.mmiServiceCode = mmi ?
|
||||
this._serviceCodeToKeyString(mmi.serviceCode) : MMI_KS_SC_USSD;
|
||||
|
||||
function _isValidPINPUKRequest() {
|
||||
// The only allowed MMI procedure for ICC PIN, PIN2, PUK and PUK2 handling
|
||||
// is "Registration" (**).
|
||||
@ -3576,36 +3363,34 @@ RilObject.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let mmiServiceCode = options.mmiServiceCode;
|
||||
let serviceCode = options.mmi.serviceCode;
|
||||
|
||||
if (options.success) {
|
||||
switch (mmiServiceCode) {
|
||||
case MMI_KS_SC_PIN:
|
||||
switch (serviceCode) {
|
||||
case MMI_SC_PIN:
|
||||
options.statusMessage = MMI_SM_KS_PIN_CHANGED;
|
||||
break;
|
||||
case MMI_KS_SC_PIN2:
|
||||
case MMI_SC_PIN2:
|
||||
options.statusMessage = MMI_SM_KS_PIN2_CHANGED;
|
||||
break;
|
||||
case MMI_KS_SC_PUK:
|
||||
case MMI_SC_PUK:
|
||||
options.statusMessage = MMI_SM_KS_PIN_UNBLOCKED;
|
||||
break;
|
||||
case MMI_KS_SC_PUK2:
|
||||
case MMI_SC_PUK2:
|
||||
options.statusMessage = MMI_SM_KS_PIN2_UNBLOCKED;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (options.retryCount <= 0) {
|
||||
if (mmiServiceCode === MMI_KS_SC_PUK) {
|
||||
if (serviceCode === MMI_SC_PUK) {
|
||||
options.errorMsg = MMI_ERROR_KS_SIM_BLOCKED;
|
||||
} else if (mmiServiceCode === MMI_KS_SC_PIN) {
|
||||
} else if (serviceCode === MMI_SC_PIN) {
|
||||
options.errorMsg = MMI_ERROR_KS_NEEDS_PUK;
|
||||
}
|
||||
} else {
|
||||
if (mmiServiceCode === MMI_KS_SC_PIN ||
|
||||
mmiServiceCode === MMI_KS_SC_PIN2) {
|
||||
if (serviceCode === MMI_SC_PIN || serviceCode === MMI_SC_PIN2) {
|
||||
options.errorMsg = MMI_ERROR_KS_BAD_PIN;
|
||||
} else if (mmiServiceCode === MMI_KS_SC_PUK ||
|
||||
mmiServiceCode === MMI_KS_SC_PUK2) {
|
||||
} else if (serviceCode === MMI_SC_PUK || serviceCode === MMI_SC_PUK2) {
|
||||
options.errorMsg = MMI_ERROR_KS_BAD_PUK;
|
||||
}
|
||||
if (options.retryCount !== undefined) {
|
||||
@ -6030,7 +5815,7 @@ RilObject.prototype[REQUEST_QUERY_CALL_FORWARD_STATUS] =
|
||||
this.sendChromeMessage(options);
|
||||
};
|
||||
RilObject.prototype[REQUEST_SET_CALL_FORWARD] =
|
||||
function REQUEST_SET_CALL_FORWARD(length, options) {
|
||||
function REQUEST_SET_CALL_FORWARD(length, options) {
|
||||
options.success = (options.rilRequestError === 0);
|
||||
if (!options.success) {
|
||||
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
|
||||
|
@ -54,6 +54,16 @@ const AUDIO_STATE_NAME = [
|
||||
|
||||
const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
|
||||
|
||||
// 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");
|
||||
@ -99,15 +109,52 @@ XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gGonkMobileConnectionService",
|
||||
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
|
||||
"nsIGonkMobileConnectionService");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function() {
|
||||
let ns = {};
|
||||
Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
|
||||
return ns.PhoneNumberUtils;
|
||||
});
|
||||
|
||||
function MMIResult(aMmiServiceCode, aOptions) {
|
||||
this.serviceCode = aMmiServiceCode;
|
||||
this.statusMessage = aOptions.statusMessage;
|
||||
this.additionalInformation = aOptions.additionalInformation;
|
||||
}
|
||||
MMIResult.prototype = {
|
||||
__exposedProps__ : {serviceCode: 'r',
|
||||
statusMessage: 'r',
|
||||
additionalInformation: 'r'},
|
||||
};
|
||||
|
||||
function CallForwardingOptions(aOptions) {
|
||||
this.active = aOptions.active;
|
||||
this.action = aOptions.action;
|
||||
this.reason = aOptions.reason;
|
||||
this.number = aOptions.number;
|
||||
this.timeSeconds = aOptions.timeSeconds;
|
||||
this.serviceClass = aOptions.serviceClass;
|
||||
}
|
||||
CallForwardingOptions.prototype = {
|
||||
__exposedProps__ : {active: 'r',
|
||||
action: 'r',
|
||||
reason: 'r',
|
||||
number: 'r',
|
||||
timeSeconds: 'r',
|
||||
serviceClass: 'r'},
|
||||
};
|
||||
|
||||
function TelephonyService() {
|
||||
this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
|
||||
this._listeners = [];
|
||||
|
||||
this._mmiRegExp = null;
|
||||
|
||||
this._isDialing = false;
|
||||
this._cachedDialRequest = null;
|
||||
this._currentCalls = {};
|
||||
|
||||
this._cdmaCallWaitingNumber = null;
|
||||
@ -300,6 +347,10 @@ TelephonyService.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_rulesToCallForwardingOptions: function(aRules) {
|
||||
return aRules.map(rule => new CallForwardingOptions(rule));
|
||||
},
|
||||
|
||||
_updateDebugFlag: function() {
|
||||
try {
|
||||
DEBUG = RIL.DEBUG_RIL ||
|
||||
@ -499,36 +550,51 @@ TelephonyService.prototype = {
|
||||
this.notifyCallStateChanged(aClientId, parentCall);
|
||||
},
|
||||
|
||||
_composeDialRequest: function(aClientId, aNumber) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._sendToRilWorker(aClientId, "parseMMIFromDialNumber",
|
||||
{number: aNumber}, response => {
|
||||
let options = {};
|
||||
let mmi = response.mmi;
|
||||
|
||||
if (!mmi) {
|
||||
resolve({
|
||||
number: aNumber
|
||||
});
|
||||
} else if (this._isTemporaryCLIR(mmi)) {
|
||||
resolve({
|
||||
number: mmi.dialNumber,
|
||||
clirMode: this._getTemporaryCLIRMode(mmi.procedure)
|
||||
});
|
||||
} else {
|
||||
reject(DIAL_ERROR_BAD_NUMBER);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
cachedDialRequest: null,
|
||||
isDialing: false,
|
||||
|
||||
dial: function(aClientId, aNumber, aIsDialEmergency, aCallback) {
|
||||
if (DEBUG) debug("Dialing " + (aIsDialEmergency ? "emergency " : "") + aNumber);
|
||||
|
||||
if (this.isDialing) {
|
||||
// We don't try to be too clever here, as the phone is probably in the
|
||||
// locked state. Let's just check if it's a number without normalizing
|
||||
if (!aIsDialEmergency) {
|
||||
aNumber = gPhoneNumberUtils.normalize(aNumber);
|
||||
}
|
||||
|
||||
// Validate the number.
|
||||
// Note: isPlainPhoneNumber also accepts USSD and SS numbers
|
||||
if (!gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) {
|
||||
if (DEBUG) debug("Error: Number '" + aNumber + "' is not viable. Drop.");
|
||||
aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
|
||||
return;
|
||||
}
|
||||
|
||||
let mmi = this._parseMMI(aClientId, aNumber);
|
||||
if (!mmi) {
|
||||
this._dialCall(aClientId,
|
||||
{ number: aNumber,
|
||||
isDialEmergency: aIsDialEmergency }, aCallback);
|
||||
} else if (this._isTemporaryCLIR(mmi)) {
|
||||
this._dialCall(aClientId,
|
||||
{ number: mmi.dialNumber,
|
||||
clirMode: this._getTemporaryCLIRMode(mmi.procedure),
|
||||
isDialEmergency: aIsDialEmergency }, aCallback);
|
||||
} else {
|
||||
// Reject MMI code from dialEmergency api.
|
||||
if (aIsDialEmergency) {
|
||||
aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
|
||||
return;
|
||||
}
|
||||
|
||||
this._dialMMI(aClientId, mmi, aCallback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param aOptions.number
|
||||
* @param aOptions.clirMode (optional)
|
||||
* @param aOptions.isDialEmergency
|
||||
*/
|
||||
_dialCall: function(aClientId, aOptions, aCallback) {
|
||||
if (this._isDialing) {
|
||||
if (DEBUG) debug("Error: Already has a dialing call.");
|
||||
aCallback.notifyDialError(DIAL_ERROR_INVALID_STATE_ERROR);
|
||||
return;
|
||||
@ -549,63 +615,43 @@ TelephonyService.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't try to be too clever here, as the phone is probably in the
|
||||
// locked state. Let's just check if it's a number without normalizing
|
||||
if (!aIsDialEmergency) {
|
||||
aNumber = gPhoneNumberUtils.normalize(aNumber);
|
||||
}
|
||||
|
||||
// Validate the number.
|
||||
// Note: isPlainPhoneNumber also accepts USSD and SS numbers
|
||||
if (!gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) {
|
||||
if (DEBUG) debug("Error: Number '" + aNumber + "' is not viable. Drop.");
|
||||
aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
|
||||
return;
|
||||
}
|
||||
|
||||
this._composeDialRequest(aClientId, aNumber).then(options => {
|
||||
options.isEmergency = this._isEmergencyNumber(options.number);
|
||||
options.isDialEmergency = aIsDialEmergency;
|
||||
|
||||
if (options.isEmergency) {
|
||||
// Automatically select a proper clientId for emergency call.
|
||||
aClientId = gRadioInterfaceLayer.getClientIdForEmergencyCall() ;
|
||||
if (aClientId === -1) {
|
||||
if (DEBUG) debug("Error: No client is avaialble for emergency call.");
|
||||
aCallback.notifyDialError(DIAL_ERROR_INVALID_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
aOptions.isEmergency = this._isEmergencyNumber(aOptions.number);
|
||||
if (aOptions.isEmergency) {
|
||||
// Automatically select a proper clientId for emergency call.
|
||||
aClientId = gRadioInterfaceLayer.getClientIdForEmergencyCall() ;
|
||||
if (aClientId === -1) {
|
||||
if (DEBUG) debug("Error: No client is avaialble for emergency call.");
|
||||
aCallback.notifyDialError(DIAL_ERROR_INVALID_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Before we dial, we have to hold the active call first.
|
||||
let activeCall = this._getOneActiveCall(aClientId);
|
||||
if (!activeCall) {
|
||||
this._dialInternal(aClientId, options, aCallback);
|
||||
// Before we dial, we have to hold the active call first.
|
||||
let activeCall = this._getOneActiveCall(aClientId);
|
||||
if (!activeCall) {
|
||||
this._sendDialCallRequest(aClientId, aOptions, aCallback);
|
||||
} else {
|
||||
if (DEBUG) debug("There is an active call. Hold it first before dial.");
|
||||
|
||||
this._cachedDialRequest = {
|
||||
clientId: aClientId,
|
||||
options: aOptions,
|
||||
callback: aCallback
|
||||
};
|
||||
|
||||
if (activeCall.isConference) {
|
||||
this.holdConference(aClientId);
|
||||
} else {
|
||||
if (DEBUG) debug("There is an active call. Hold it first before dial.");
|
||||
|
||||
this.cachedDialRequest = {
|
||||
clientId: aClientId,
|
||||
options: options,
|
||||
callback: aCallback
|
||||
};
|
||||
|
||||
if (activeCall.isConference) {
|
||||
this.holdConference(aClientId);
|
||||
} else {
|
||||
this.holdCall(aClientId, activeCall.callIndex);
|
||||
}
|
||||
this.holdCall(aClientId, activeCall.callIndex);
|
||||
}
|
||||
}, cause => {
|
||||
aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_dialInternal: function(aClientId, aOptions, aCallback) {
|
||||
this.isDialing = true;
|
||||
_sendDialCallRequest: function(aClientId, aOptions, aCallback) {
|
||||
this._isDialing = true;
|
||||
|
||||
this._sendToRilWorker(aClientId, "dial", aOptions, response => {
|
||||
this.isDialing = false;
|
||||
this._isDialing = false;
|
||||
|
||||
if (!response.success) {
|
||||
aCallback.notifyDialError(response.errorMsg);
|
||||
@ -625,6 +671,235 @@ TelephonyService.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param aMmi
|
||||
* Parsed MMI structure.
|
||||
*/
|
||||
_dialMMI: function(aClientId, aMmi, aCallback) {
|
||||
let mmiServiceCode = aMmi ?
|
||||
this._serviceCodeToKeyString(aMmi.serviceCode) : RIL.MMI_KS_SC_USSD;
|
||||
|
||||
aCallback.notifyDialMMI(mmiServiceCode);
|
||||
|
||||
this._sendToRilWorker(aClientId, "sendMMI", { mmi: aMmi }, response => {
|
||||
if (DEBUG) debug("MMI response: " + JSON.stringify(response));
|
||||
|
||||
if (!response.success) {
|
||||
if (response.additionalInformation != null) {
|
||||
aCallback.notifyDialMMIErrorWithInfo(response.errorMsg,
|
||||
response.additionalInformation);
|
||||
} else {
|
||||
aCallback.notifyDialMMIError(response.errorMsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We expect to have an IMEI at this point if the request was supposed
|
||||
// to query for the IMEI, so getting a successful reply from the RIL
|
||||
// without containing an actual IMEI number is considered an error.
|
||||
if (mmiServiceCode === RIL.MMI_KS_SC_IMEI &&
|
||||
!response.statusMessage) {
|
||||
aCallback.notifyDialMMIError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// MMI query call forwarding options request returns a set of rules that
|
||||
// will be exposed in the form of an array of MozCallForwardingOptions
|
||||
// instances.
|
||||
if (mmiServiceCode === RIL.MMI_KS_SC_CALL_FORWARDING) {
|
||||
if (response.isSetCallForward) {
|
||||
gGonkMobileConnectionService.notifyCFStateChanged(aClientId,
|
||||
response.action,
|
||||
response.reason,
|
||||
response.number,
|
||||
response.timeSeconds,
|
||||
response.serviceClass);
|
||||
}
|
||||
|
||||
if (response.additionalInformation != null) {
|
||||
response.additionalInformation =
|
||||
this._rulesToCallForwardingOptions(response.additionalInformation);
|
||||
}
|
||||
}
|
||||
|
||||
let result = new MMIResult(mmiServiceCode, response);
|
||||
aCallback.notifyDialMMISuccess(result);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 dialString = "([^#]*)";
|
||||
|
||||
return new RegExp(fullmmi + dialString);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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(aClientId, aMmiString) {
|
||||
if (aMmiString.length > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// In a call case.
|
||||
if (Object.getOwnPropertyNames(this._currentCalls[aClientId]).length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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(aClientId, aMmiString) {
|
||||
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(aClientId, aMmiString)) {
|
||||
return {
|
||||
fullMMI: aMmiString
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_serviceCodeToKeyString: function(aServiceCode) {
|
||||
switch (aServiceCode) {
|
||||
case RIL.MMI_SC_CFU:
|
||||
case RIL.MMI_SC_CF_BUSY:
|
||||
case RIL.MMI_SC_CF_NO_REPLY:
|
||||
case RIL.MMI_SC_CF_NOT_REACHABLE:
|
||||
case RIL.MMI_SC_CF_ALL:
|
||||
case RIL.MMI_SC_CF_ALL_CONDITIONAL:
|
||||
return RIL.MMI_KS_SC_CALL_FORWARDING;
|
||||
case RIL.MMI_SC_PIN:
|
||||
return RIL.MMI_KS_SC_PIN;
|
||||
case RIL.MMI_SC_PIN2:
|
||||
return RIL.MMI_KS_SC_PIN2;
|
||||
case RIL.MMI_SC_PUK:
|
||||
return RIL.MMI_KS_SC_PUK;
|
||||
case RIL.MMI_SC_PUK2:
|
||||
return RIL.MMI_KS_SC_PUK2;
|
||||
case RIL.MMI_SC_IMEI:
|
||||
return RIL.MMI_KS_SC_IMEI;
|
||||
case RIL.MMI_SC_CLIP:
|
||||
return RIL.MMI_KS_SC_CLIP;
|
||||
case RIL.MMI_SC_CLIR:
|
||||
return RIL.MMI_KS_SC_CLIR;
|
||||
case RIL.MMI_SC_BAOC:
|
||||
case RIL.MMI_SC_BAOIC:
|
||||
case RIL.MMI_SC_BAOICxH:
|
||||
case RIL.MMI_SC_BAIC:
|
||||
case RIL.MMI_SC_BAICr:
|
||||
case RIL.MMI_SC_BA_ALL:
|
||||
case RIL.MMI_SC_BA_MO:
|
||||
case RIL.MMI_SC_BA_MT:
|
||||
return RIL.MMI_KS_SC_CALL_BARRING;
|
||||
case RIL.MMI_SC_CALL_WAITING:
|
||||
return RIL.MMI_KS_SC_CALL_WAITING;
|
||||
default:
|
||||
return RIL.MMI_KS_SC_USSD;
|
||||
}
|
||||
},
|
||||
|
||||
hangUp: function(aClientId, aCallIndex) {
|
||||
let parentId = this._currentCalls[aClientId][aCallIndex].parentId;
|
||||
if (parentId) {
|
||||
@ -932,12 +1207,12 @@ TelephonyService.prototype = {
|
||||
}
|
||||
|
||||
// Handle cached dial request.
|
||||
if (this.cachedDialRequest && !this._getOneActiveCall()) {
|
||||
if (this._cachedDialRequest && !this._getOneActiveCall()) {
|
||||
if (DEBUG) debug("All calls held. Perform the cached dial request.");
|
||||
|
||||
let request = this.cachedDialRequest;
|
||||
this._dialInternal(request.clientId, request.options, request.callback);
|
||||
this.cachedDialRequest = null;
|
||||
let request = this._cachedDialRequest;
|
||||
this._sendDialCallRequest(request.clientId, request.options, request.callback);
|
||||
this._cachedDialRequest = null;
|
||||
}
|
||||
|
||||
this._notifyAllListeners("callStateChanged", [aClientId,
|
||||
@ -987,6 +1262,11 @@ TelephonyService.prototype = {
|
||||
this._notifyAllListeners("conferenceCallStateChanged", [aState]);
|
||||
},
|
||||
|
||||
dialMMI: function(aClientId, aMmiString, aCallback) {
|
||||
let mmi = this._parseMMI(aClientId, aMmiString);
|
||||
this._dialMMI(aClientId, mmi, aCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIObserver interface.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user