diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index 28017c456aac..62f2befb61e3 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 13e4774ead69..33becd992411 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index dcc227a7fe1e..622788c408ca 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 8f917efbaa10..0568fb0743af 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,10 +17,10 @@ - + - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 37cbdb424350..0321294e710f 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index e2011c3167e0..ea19e322a22b 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index dcc227a7fe1e..622788c408ca 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index c4f231c93902..70c8751ce694 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index fa5d5470e56e..db467fe77dee 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "77bc0d940bde2a5d2d4dfadfcccc6d8d77456d36", + "git_revision": "8c009877aff6b8b2f4a60756e2d09c0182393721", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "dfd4a9e8f31e64d427030d3612a48f7dbcada5d3", + "revision": "494ef969c9ddbf15fc8a094c2da7bc46d08b1fc3", "repo_path": "integration/gaia-central" } diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 3c3f35308c4f..47885ccc7465 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,10 +17,10 @@ - + - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 4c57228ad75f..196aa9015986 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/dom/system/gonk/SystemWorkerManager.cpp b/dom/system/gonk/SystemWorkerManager.cpp index a5622f57c995..4ae4baac50ef 100644 --- a/dom/system/gonk/SystemWorkerManager.cpp +++ b/dom/system/gonk/SystemWorkerManager.cpp @@ -119,7 +119,7 @@ SystemWorkerManager::Shutdown() ShutdownAutoMounter(); #ifdef MOZ_B2G_RIL - RilConsumer::Shutdown(); + RilWorker::Shutdown(); #endif nsCOMPtr wifi(do_QueryInterface(mWifiWorker)); @@ -201,7 +201,7 @@ SystemWorkerManager::RegisterRilWorker(unsigned int aClientId, return NS_ERROR_FAILURE; } - return RilConsumer::Register(aClientId, wctd); + return RilWorker::Register(aClientId, wctd); #endif // MOZ_B2G_RIL } diff --git a/dom/system/gonk/ril_consts.js b/dom/system/gonk/ril_consts.js index 7c2f5f082f93..d446b8de7cd9 100644 --- a/dom/system/gonk/ril_consts.js +++ b/dom/system/gonk/ril_consts.js @@ -2976,151 +2976,6 @@ CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_ALL_SERVICE] = ICC_CB_FACI CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_OUTGOING_SERVICE] = ICC_CB_FACILITY_BA_MO; CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_INCOMING_SERVICE] = ICC_CB_FACILITY_BA_MT; -// CLIR constants. Must be in sync with nsIMobileConnectionService interface -this.CLIR_DEFAULT = 0; -this.CLIR_INVOCATION = 1; -this.CLIR_SUPPRESSION = 2; - -// MMI procedure as defined in TS.22.030 6.5.2 -this.MMI_PROCEDURE_ACTIVATION = "*"; -this.MMI_PROCEDURE_DEACTIVATION = "#"; -this.MMI_PROCEDURE_INTERROGATION = "*#"; -this.MMI_PROCEDURE_REGISTRATION = "**"; -this.MMI_PROCEDURE_ERASURE = "##"; - -this.MMI_PROC_TO_CF_ACTION = {}; -MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_ACTIVATION] = CALL_FORWARD_ACTION_ENABLE; -MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_DEACTIVATION] = CALL_FORWARD_ACTION_DISABLE; -MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_INTERROGATION] = CALL_FORWARD_ACTION_QUERY_STATUS; -MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_REGISTRATION] = CALL_FORWARD_ACTION_REGISTRATION; -MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_ERASURE] = CALL_FORWARD_ACTION_ERASURE; - -// MMI call forwarding service codes as defined in TS.22.030 Annex B -this.MMI_SC_CFU = "21"; -this.MMI_SC_CF_BUSY = "67"; -this.MMI_SC_CF_NO_REPLY = "61"; -this.MMI_SC_CF_NOT_REACHABLE = "62"; -this.MMI_SC_CF_ALL = "002"; -this.MMI_SC_CF_ALL_CONDITIONAL = "004"; - -this.MMI_SC_TO_CF_REASON = {}; -MMI_SC_TO_CF_REASON[MMI_SC_CFU] = CALL_FORWARD_REASON_UNCONDITIONAL; -MMI_SC_TO_CF_REASON[MMI_SC_CF_BUSY] = CALL_FORWARD_REASON_MOBILE_BUSY; -MMI_SC_TO_CF_REASON[MMI_SC_CF_NO_REPLY] = CALL_FORWARD_REASON_NO_REPLY; -MMI_SC_TO_CF_REASON[MMI_SC_CF_NOT_REACHABLE] = CALL_FORWARD_REASON_NOT_REACHABLE; -MMI_SC_TO_CF_REASON[MMI_SC_CF_ALL] = CALL_FORWARD_REASON_ALL_CALL_FORWARDING; -MMI_SC_TO_CF_REASON[MMI_SC_CF_ALL_CONDITIONAL] = CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING; - -// MMI service codes for PIN/PIN2/PUK/PUK2 management as defined in TS.22.030 -// sec 6.6 -this.MMI_SC_PIN = "04"; -this.MMI_SC_PIN2 = "042"; -this.MMI_SC_PUK = "05"; -this.MMI_SC_PUK2 = "052"; - -// MMI service code for IMEI presentation as defined in TS.22.030 sec 6.7 -this.MMI_SC_IMEI = "06"; - -// MMI called line presentation service codes -this.MMI_SC_CLIP = "30"; -this.MMI_SC_CLIR = "31"; - -// MMI call waiting service code -this.MMI_SC_CALL_WAITING = "43"; - -// MMI service code for registration new password as defined in TS 22.030 6.5.4 -this.MMI_SC_CHANGE_PASSWORD = "03"; -this.MMI_ZZ_BARRING_SERVICE = "330"; - -// MMI call barring service codes -this.MMI_SC_BAOC = "33"; -this.MMI_SC_BAOIC = "331"; -this.MMI_SC_BAOICxH = "332"; -this.MMI_SC_BAIC = "35"; -this.MMI_SC_BAICr = "351"; -this.MMI_SC_BA_ALL = "330"; -this.MMI_SC_BA_MO = "333"; -this.MMI_SC_BA_MT = "353"; - -this.MMI_SC_TO_CB_FACILITY = {}; - -MMI_SC_TO_CB_FACILITY[MMI_SC_BAOC] = ICC_CB_FACILITY_BAOC; -MMI_SC_TO_CB_FACILITY[MMI_SC_BAOIC] = ICC_CB_FACILITY_BAOIC; -MMI_SC_TO_CB_FACILITY[MMI_SC_BAOICxH] = ICC_CB_FACILITY_BAOICxH; -MMI_SC_TO_CB_FACILITY[MMI_SC_BAIC] = ICC_CB_FACILITY_BAIC; -MMI_SC_TO_CB_FACILITY[MMI_SC_BAICr] = ICC_CB_FACILITY_BAICr; -MMI_SC_TO_CB_FACILITY[MMI_SC_BA_ALL] = ICC_CB_FACILITY_BA_ALL; -MMI_SC_TO_CB_FACILITY[MMI_SC_BA_MO] = ICC_CB_FACILITY_BA_MO; -MMI_SC_TO_CB_FACILITY[MMI_SC_BA_MT] = ICC_CB_FACILITY_BA_MT; - -// MMI service code key strings. -this.MMI_KS_SC_CALL_BARRING = "scCallBarring"; -this.MMI_KS_SC_CALL_FORWARDING = "scCallForwarding"; -this.MMI_KS_SC_CLIP = "scClip"; -this.MMI_KS_SC_CLIR = "scClir"; -this.MMI_KS_SC_PWD = "scPwd"; -this.MMI_KS_SC_CALL_WAITING = "scCallWaiting"; -this.MMI_KS_SC_PIN = "scPin"; -this.MMI_KS_SC_PIN2 = "scPin2"; -this.MMI_KS_SC_PUK = "scPuk"; -this.MMI_KS_SC_PUK2 = "scPuk2"; -this.MMI_KS_SC_CHANGE_PASSWORD = "scChangePassword"; -this.MMI_KS_SC_IMEI = "scImei"; -this.MMI_KS_SC_USSD = "scUssd"; -this.MMI_KS_SC_CALL = "scCall"; - -// MMI error messages key strings. -this.MMI_ERROR_KS_ERROR = "emMmiError"; -this.MMI_ERROR_KS_NOT_SUPPORTED = "emMmiErrorNotSupported"; -this.MMI_ERROR_KS_INVALID_ACTION = "emMmiErrorInvalidAction"; -this.MMI_ERROR_KS_MISMATCH_PIN = "emMmiErrorMismatchPin"; -this.MMI_ERROR_KS_MISMATCH_PASSWORD = "emMmiErrorMismatchPassword"; -this.MMI_ERROR_KS_BAD_PIN = "emMmiErrorBadPin"; -this.MMI_ERROR_KS_BAD_PUK = "emMmiErrorBadPuk"; -this.MMI_ERROR_KS_INVALID_PIN = "emMmiErrorInvalidPin"; -this.MMI_ERROR_KS_INVALID_PASSWORD = "emMmiErrorInvalidPassword"; -this.MMI_ERROR_KS_NEEDS_PUK = "emMmiErrorNeedsPuk"; -this.MMI_ERROR_KS_SIM_BLOCKED = "emMmiErrorSimBlocked"; - -// MMI status message. -this.MMI_SM_KS_PASSWORD_CHANGED = "smPasswordChanged"; -this.MMI_SM_KS_PIN_CHANGED = "smPinChanged"; -this.MMI_SM_KS_PIN2_CHANGED = "smPin2Changed"; -this.MMI_SM_KS_PIN_UNBLOCKED = "smPinUnblocked"; -this.MMI_SM_KS_PIN2_UNBLOCKED = "smPin2Unblocked"; -this.MMI_SM_KS_SERVICE_ENABLED = "smServiceEnabled"; -this.MMI_SM_KS_SERVICE_ENABLED_FOR = "smServiceEnabledFor"; -this.MMI_SM_KS_SERVICE_DISABLED = "smServiceDisabled"; -this.MMI_SM_KS_SERVICE_REGISTERED = "smServiceRegistered"; -this.MMI_SM_KS_SERVICE_ERASED = "smServiceErased"; -this.MMI_SM_KS_SERVICE_INTERROGATED = "smServiceInterrogated"; -this.MMI_SM_KS_SERVICE_NOT_PROVISIONED = "smServiceNotProvisioned"; -this.MMI_SM_KS_CLIR_PERMANENT = "smClirPermanent"; -this.MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_ON = "smClirDefaultOnNextCallOn"; -this.MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_OFF = "smClirDefaultOnNextCallOff"; -this.MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_ON = "smClirDefaultOffNextCallOn"; -this.MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_OFF = "smClirDefaultOffNextCallOff"; -this.MMI_SM_KS_CALL_CONTROL = "smCallControl"; - -// MMI Service class -this.MMI_KS_SERVICE_CLASS_VOICE = "serviceClassVoice"; -this.MMI_KS_SERVICE_CLASS_DATA = "serviceClassData"; -this.MMI_KS_SERVICE_CLASS_FAX = "serviceClassFax"; -this.MMI_KS_SERVICE_CLASS_SMS = "serviceClassSms"; -this.MMI_KS_SERVICE_CLASS_DATA_SYNC = "serviceClassDataSync"; -this.MMI_KS_SERVICE_CLASS_DATA_ASYNC = "serviceClassDataAsync"; -this.MMI_KS_SERVICE_CLASS_PACKET = "serviceClassPacket"; -this.MMI_KS_SERVICE_CLASS_PAD = "serviceClassPad"; - -this.MMI_KS_SERVICE_CLASS_MAPPING = {}; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_VOICE] = MMI_KS_SERVICE_CLASS_VOICE; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_DATA] = MMI_KS_SERVICE_CLASS_DATA; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_FAX] = MMI_KS_SERVICE_CLASS_FAX; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_SMS] = MMI_KS_SERVICE_CLASS_SMS; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_DATA_SYNC] = MMI_KS_SERVICE_CLASS_DATA_SYNC; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_DATA_ASYNC] = MMI_KS_SERVICE_CLASS_DATA_ASYNC; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_PACKET] = MMI_KS_SERVICE_CLASS_PACKET; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_PAD] = MMI_KS_SERVICE_CLASS_PAD; /** * CDMA PDU constants */ diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index a3dfa8279e86..79479efbc248 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -244,16 +244,6 @@ RilObject.prototype = { */ this._pendingNetworkInfo = {rilMessageType: "networkinfochanged"}; - /** - * USSD session flag. - * Only one USSD session may exist at a time, and the session is assumed - * to exist until: - * a) There's a call to cancelUSSD() - * b) The implementation sends a UNSOLICITED_ON_USSD with a type code - * of "0" (USSD-Notify/no further action) or "2" (session terminated) - */ - this._ussdSession = null; - /** * Cell Broadcast Search Lists. */ @@ -870,60 +860,6 @@ RilObject.prototype = { Buf.sendParcel(); }, - /** - * Query call waiting status via MMI. - */ - _handleQueryMMICallWaiting: function(options) { - let Buf = this.context.Buf; - - function callback(options) { - options.length = Buf.readInt32(); - options.enabled = (Buf.readInt32() === 1); - let services = Buf.readInt32(); - if (options.enabled) { - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED_FOR; - let serviceClass = []; - for (let serviceClassMask = 1; - serviceClassMask <= ICC_SERVICE_CLASS_MAX; - serviceClassMask <<= 1) { - if ((serviceClassMask & services) !== 0) { - serviceClass.push(MMI_KS_SERVICE_CLASS_MAPPING[serviceClassMask]); - } - } - options.additionalInformation = serviceClass; - } else { - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - } - - // Prevent DataCloneError when sending chrome messages. - delete options.callback; - this.sendChromeMessage(options); - } - - options.callback = callback; - this.queryCallWaiting(options); - }, - - /** - * Set call waiting status via MMI. - */ - _handleSetMMICallWaiting: function(options) { - function callback(options) { - if (options.enabled) { - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED; - } else { - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - } - - // Prevent DataCloneError when sending chrome messages. - delete options.callback; - this.sendChromeMessage(options); - } - - options.callback = callback; - this.setCallWaiting(options); - }, - /** * Query call waiting status. * @@ -957,9 +893,6 @@ RilObject.prototype = { /** * Queries current CLIP status. - * - * (MMI request for code "*#30#") - * */ queryCLIP: function(options) { this.context.Buf.simpleRequest(REQUEST_QUERY_CLIP, options); @@ -1239,6 +1172,15 @@ RilObject.prototype = { }, getIMEI: function(options) { + // A device's IMEI can't change, so we only need to request it once. + if (this.IMEI) { + if (options && options.rilMessageType) { + options.imei = this.IMEI; + this.sendChromeMessage(options); + } + return; + } + this.context.Buf.simpleRequest(REQUEST_GET_IMEI, options); }, @@ -1893,325 +1835,13 @@ RilObject.prototype = { this.context.Buf.simpleRequest(REQUEST_LAST_CALL_FAIL_CAUSE, options); }, - sendMMI: function(options) { - if (DEBUG) { - this.context.debug("SendMMI " + JSON.stringify(options)); - } - - let _sendMMIError = (function(errorMsg) { - options.errorMsg = errorMsg; - this.sendChromeMessage(options); - }).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; - } - - function _isValidPINPUKRequest() { - // The only allowed MMI procedure for ICC PIN, PIN2, PUK and PUK2 handling - // is "Registration" (**). - if (mmi.procedure != MMI_PROCEDURE_REGISTRATION ) { - _sendMMIError(MMI_ERROR_KS_INVALID_ACTION); - return false; - } - - if (!mmi.sia || !mmi.sib || !mmi.sic) { - _sendMMIError(MMI_ERROR_KS_ERROR); - return false; - } - - if (mmi.sia.length < 4 || mmi.sia.length > 8 || - mmi.sib.length < 4 || mmi.sib.length > 8 || - mmi.sic.length < 4 || mmi.sic.length > 8) { - _sendMMIError(MMI_ERROR_KS_INVALID_PIN); - return false; - } - - if (mmi.sib != mmi.sic) { - _sendMMIError(MMI_ERROR_KS_MISMATCH_PIN); - return false; - } - - return true; - } - - function _isValidChangePasswordRequest() { - if (mmi.procedure !== MMI_PROCEDURE_REGISTRATION && - mmi.procedure !== MMI_PROCEDURE_ACTIVATION) { - _sendMMIError(MMI_ERROR_KS_INVALID_ACTION); - return false; - } - - if (mmi.sia !== "" && mmi.sia !== MMI_ZZ_BARRING_SERVICE) { - _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); - return false; - } - - let validPassword = si => /^[0-9]{4}$/.test(si); - if (!validPassword(mmi.sib) || !validPassword(mmi.sic) || - !validPassword(mmi.pwd)) { - _sendMMIError(MMI_ERROR_KS_INVALID_PASSWORD); - return false; - } - - if (mmi.sic != mmi.pwd) { - _sendMMIError(MMI_ERROR_KS_MISMATCH_PASSWORD); - return false; - } - - return true; - } - - let _isRadioAvailable = (function() { - if (this.radioState !== GECKO_RADIOSTATE_ENABLED) { - _sendMMIError(GECKO_ERROR_RADIO_NOT_AVAILABLE); - return false; - } - return true; - }).bind(this); - - // We check if the MMI service code is supported and in that case we - // trigger the appropriate RIL request if possible. - let sc = mmi.serviceCode; - switch (sc) { - // Call forwarding - 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: - if (!_isRadioAvailable()) { - return; - } - // Call forwarding requires at least an action, given by the MMI - // procedure, and a reason, given by the MMI service code, but there - // is no way that we get this far without a valid procedure or service - // code. - options.action = MMI_PROC_TO_CF_ACTION[mmi.procedure]; - options.reason = MMI_SC_TO_CF_REASON[sc]; - options.number = mmi.sia; - options.serviceClass = this._siToServiceClass(mmi.sib); - if (options.action == CALL_FORWARD_ACTION_QUERY_STATUS) { - this.queryCallForwardStatus(options); - return; - } - - options.isSetCallForward = true; - options.timeSeconds = mmi.sic; - this.setCallForward(options); - return; - - // Change the current ICC PIN number. - case MMI_SC_PIN: - // As defined in TS.122.030 6.6.2 to change the ICC PIN we should expect - // an MMI code of the form **04*OLD_PIN*NEW_PIN*NEW_PIN#, where old PIN - // should be entered as the SIA parameter and the new PIN as SIB and - // SIC. - if (!_isRadioAvailable() || !_isValidPINPUKRequest()) { - return; - } - - options.password = mmi.sia; - options.newPassword = mmi.sib; - this.changeICCPIN(options); - return; - - // Change the current ICC PIN2 number. - case MMI_SC_PIN2: - // As defined in TS.122.030 6.6.2 to change the ICC PIN2 we should - // enter and MMI code of the form **042*OLD_PIN2*NEW_PIN2*NEW_PIN2#, - // where the old PIN2 should be entered as the SIA parameter and the - // new PIN2 as SIB and SIC. - if (!_isRadioAvailable() || !_isValidPINPUKRequest()) { - return; - } - - options.password = mmi.sia; - options.newPassword = mmi.sib; - this.changeICCPIN2(options); - return; - - // Unblock ICC PIN. - case MMI_SC_PUK: - // As defined in TS.122.030 6.6.3 to unblock the ICC PIN we should - // enter an MMI code of the form **05*PUK*NEW_PIN*NEW_PIN#, where PUK - // should be entered as the SIA parameter and the new PIN as SIB and - // SIC. - if (!_isRadioAvailable() || !_isValidPINPUKRequest()) { - return; - } - - options.password = mmi.sia; - options.newPin = mmi.sib; - this.enterICCPUK(options); - return; - - // Unblock ICC PIN2. - case MMI_SC_PUK2: - // As defined in TS.122.030 6.6.3 to unblock the ICC PIN2 we should - // enter an MMI code of the form **052*PUK2*NEW_PIN2*NEW_PIN2#, where - // PUK2 should be entered as the SIA parameter and the new PIN2 as SIB - // and SIC. - if (!_isRadioAvailable() || !_isValidPINPUKRequest()) { - return; - } - - options.password = mmi.sia; - options.newPin = mmi.sib; - this.enterICCPUK2(options); - return; - - // IMEI - case MMI_SC_IMEI: - // A device's IMEI can't change, so we only need to request it once. - if (this.IMEI == null) { - this.getIMEI(options); - return; - } - // If we already had the device's IMEI, we just send it to chrome. - options.statusMessage = this.IMEI; - this.sendChromeMessage(options); - return; - - // CLIP - case MMI_SC_CLIP: - options.procedure = mmi.procedure; - if (options.procedure === MMI_PROCEDURE_INTERROGATION) { - this.queryCLIP(options); - } else { - _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); - } - return; - - // CLIR (non-temporary ones) - // TODO: Both dial() and sendMMI() functions should be unified at some - // point in the future. In the mean time we handle temporary CLIR MMI - // commands through the dial() function. Please see bug 889737. - case MMI_SC_CLIR: - options.procedure = mmi.procedure; - switch (options.procedure) { - case MMI_PROCEDURE_INTERROGATION: - this.getCLIR(options); - return; - case MMI_PROCEDURE_ACTIVATION: - options.clirMode = CLIR_INVOCATION; - break; - case MMI_PROCEDURE_DEACTIVATION: - options.clirMode = CLIR_SUPPRESSION; - break; - default: - _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); - return; - } - options.isSetCLIR = true; - this.setCLIR(options); - return; - - // Change call barring password - case MMI_SC_CHANGE_PASSWORD: - if (!_isRadioAvailable() || !_isValidChangePasswordRequest()) { - return; - } - - options.pin = mmi.sib; - options.newPin = mmi.sic; - this.changeCallBarringPassword(options); - return; - - // Call barring - 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: - options.password = mmi.sia || ""; - options.serviceClass = this._siToServiceClass(mmi.sib); - options.facility = MMI_SC_TO_CB_FACILITY[sc]; - options.procedure = mmi.procedure; - if (mmi.procedure === MMI_PROCEDURE_INTERROGATION) { - this.queryICCFacilityLock(options); - return; - } - if (mmi.procedure === MMI_PROCEDURE_ACTIVATION) { - options.enabled = 1; - } else if (mmi.procedure === MMI_PROCEDURE_DEACTIVATION) { - options.enabled = 0; - } else { - _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); - return; - } - this.setICCFacilityLock(options); - return; - - // Call waiting - case MMI_SC_CALL_WAITING: - if (!_isRadioAvailable()) { - return; - } - - - if (mmi.procedure === MMI_PROCEDURE_INTERROGATION) { - this._handleQueryMMICallWaiting(options); - return; - } - - if (mmi.procedure === MMI_PROCEDURE_ACTIVATION) { - options.enabled = true; - } else if (mmi.procedure === MMI_PROCEDURE_DEACTIVATION) { - options.enabled = false; - } else { - _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); - return; - } - - options.serviceClass = this._siToServiceClass(mmi.sia); - this._handleSetMMICallWaiting(options); - return; - } - - // If the MMI code is not a known code, it is treated as an ussd. - if (!_isRadioAvailable()) { - return; - } - - options.ussd = mmi.fullMMI; - - if (this._ussdSession) { - if (DEBUG) this.context.debug("Cancel existing ussd session."); - this.cachedUSSDRequest = options; - this.cancelUSSD({}); - return; - } - - this.sendUSSD(options, false); - }, - - /** - * Cache the request for send out a new ussd when there is an existing - * session. We should do cancelUSSD first. - */ - cachedUSSDRequest : null, - /** * Send USSD. * * @param ussd * String containing the USSD code. */ - sendUSSD: function(options, checkSession = true) { - if (checkSession && !this._ussdSession) { - options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; - this.sendChromeMessage(options); - return; - } - + sendUSSD: function(options) { let Buf = this.context.Buf; Buf.newParcel(REQUEST_SEND_USSD, options); Buf.writeString(options.ussd); @@ -2935,47 +2565,6 @@ RilObject.prototype = { */ _processEnterAndChangeICCResponses: function(length, options) { options.retryCount = length ? this.context.Buf.readInt32List()[0] : -1; - if (options.rilMessageType != "sendMMI") { - this.sendChromeMessage(options); - return; - } - - let serviceCode = options.mmi.serviceCode; - - if (!options.errorMsg) { - switch (serviceCode) { - case MMI_SC_PIN: - options.statusMessage = MMI_SM_KS_PIN_CHANGED; - break; - case MMI_SC_PIN2: - options.statusMessage = MMI_SM_KS_PIN2_CHANGED; - break; - case MMI_SC_PUK: - options.statusMessage = MMI_SM_KS_PIN_UNBLOCKED; - break; - case MMI_SC_PUK2: - options.statusMessage = MMI_SM_KS_PIN2_UNBLOCKED; - break; - } - } else { - if (options.retryCount <= 0) { - if (serviceCode === MMI_SC_PUK) { - options.errorMsg = MMI_ERROR_KS_SIM_BLOCKED; - } else if (serviceCode === MMI_SC_PIN) { - options.errorMsg = MMI_ERROR_KS_NEEDS_PUK; - } - } else { - if (serviceCode === MMI_SC_PIN || serviceCode === MMI_SC_PIN2) { - options.errorMsg = MMI_ERROR_KS_BAD_PIN; - } else if (serviceCode === MMI_SC_PUK || serviceCode === MMI_SC_PUK2) { - options.errorMsg = MMI_ERROR_KS_BAD_PUK; - } - if (options.retryCount !== undefined) { - options.additionalInformation = options.retryCount; - } - } - } - this.sendChromeMessage(options); }, @@ -3584,44 +3173,6 @@ RilObject.prototype = { return toa; }, - /** - * Helper for translating basic service group to call forwarding service class - * parameter. - */ - _siToServiceClass: function(si) { - if (!si) { - return ICC_SERVICE_CLASS_NONE; - } - - let serviceCode = parseInt(si, 10); - switch (serviceCode) { - case 10: - return ICC_SERVICE_CLASS_SMS + ICC_SERVICE_CLASS_FAX + ICC_SERVICE_CLASS_VOICE; - case 11: - return ICC_SERVICE_CLASS_VOICE; - case 12: - return ICC_SERVICE_CLASS_SMS + ICC_SERVICE_CLASS_FAX; - case 13: - return ICC_SERVICE_CLASS_FAX; - case 16: - return ICC_SERVICE_CLASS_SMS; - case 19: - return ICC_SERVICE_CLASS_FAX + ICC_SERVICE_CLASS_VOICE; - case 21: - return ICC_SERVICE_CLASS_PAD + ICC_SERVICE_CLASS_DATA_ASYNC; - case 22: - return ICC_SERVICE_CLASS_PACKET + ICC_SERVICE_CLASS_DATA_SYNC; - case 25: - return ICC_SERVICE_CLASS_DATA_ASYNC; - case 26: - return ICC_SERVICE_CLASS_DATA_SYNC + SERVICE_CLASS_VOICE; - case 99: - return ICC_SERVICE_CLASS_PACKET; - default: - return ICC_SERVICE_CLASS_NONE; - } - }, - /** * @param message A decoded SMS-DELIVER message. * @@ -4745,24 +4296,12 @@ RilObject.prototype[REQUEST_SEND_USSD] = function REQUEST_SEND_USSD(length, opti if (DEBUG) { this.context.debug("REQUEST_SEND_USSD " + JSON.stringify(options)); } - this._ussdSession = !options.errorMsg; this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_CANCEL_USSD] = function REQUEST_CANCEL_USSD(length, options) { if (DEBUG) { this.context.debug("REQUEST_CANCEL_USSD" + JSON.stringify(options)); } - - this._ussdSession = !!options.errorMsg; - - // The cancelUSSD is triggered by ril_worker itself. - if (this.cachedUSSDRequest) { - if (DEBUG) this.context.debug("Send out the cached ussd request"); - this.sendUSSD(this.cachedUSSDRequest); - this.cachedUSSDRequest = null; - return; - } - this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_GET_CLIR] = function REQUEST_GET_CLIR(length, options) { @@ -4781,66 +4320,6 @@ RilObject.prototype[REQUEST_GET_CLIR] = function REQUEST_GET_CLIR(length, option options.n = Buf.readInt32(); // Will be TS 27.007 +CLIR parameter 'n'. options.m = Buf.readInt32(); // Will be TS 27.007 +CLIR parameter 'm'. - - if (options.rilMessageType === "sendMMI") { - // TS 27.007 +CLIR parameter 'm'. - switch (options.m) { - // CLIR not provisioned. - case 0: - options.statusMessage = MMI_SM_KS_SERVICE_NOT_PROVISIONED; - break; - // CLIR provisioned in permanent mode. - case 1: - options.statusMessage = MMI_SM_KS_CLIR_PERMANENT; - break; - // Unknown (e.g. no network, etc.). - case 2: - options.errorMsg = MMI_ERROR_KS_ERROR; - break; - // CLIR temporary mode presentation restricted. - case 3: - // TS 27.007 +CLIR parameter 'n'. - switch (options.n) { - // Default. - case 0: - // CLIR invocation. - case 1: - options.statusMessage = MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_ON; - break; - // CLIR suppression. - case 2: - options.statusMessage = MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_OFF; - break; - default: - options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; - break; - } - break; - // CLIR temporary mode presentation allowed. - case 4: - // TS 27.007 +CLIR parameter 'n'. - switch (options.n) { - // Default. - case 0: - // CLIR suppression. - case 2: - options.statusMessage = MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_OFF; - break; - // CLIR invocation. - case 1: - options.statusMessage = MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_ON; - break; - default: - options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; - break; - } - break; - default: - options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; - break; - } - } - this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SET_CLIR] = function REQUEST_SET_CLIR(length, options) { @@ -4849,16 +4328,6 @@ RilObject.prototype[REQUEST_SET_CLIR] = function REQUEST_SET_CLIR(length, option return; } - if (!options.errorMsg && options.rilMessageType === "sendMMI") { - switch (options.procedure) { - case MMI_PROCEDURE_ACTIVATION: - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED; - break; - case MMI_PROCEDURE_DEACTIVATION: - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - break; - } - } this.sendChromeMessage(options); }; @@ -4891,52 +4360,19 @@ RilObject.prototype[REQUEST_QUERY_CALL_FORWARD_STATUS] = rules[i] = rule; } options.rules = rules; - if (options.rilMessageType === "sendMMI") { - options.statusMessage = MMI_SM_KS_SERVICE_INTERROGATED; - // MMI query call forwarding options request returns a set of rules that - // will be exposed in the form of an array of MozCallForwardingOptions - // instances. - options.additionalInformation = rules; - } this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SET_CALL_FORWARD] = function REQUEST_SET_CALL_FORWARD(length, options) { - if (!options.errorMsg && options.rilMessageType === "sendMMI") { - switch (options.action) { - case CALL_FORWARD_ACTION_ENABLE: - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED; - break; - case CALL_FORWARD_ACTION_DISABLE: - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - break; - case CALL_FORWARD_ACTION_REGISTRATION: - options.statusMessage = MMI_SM_KS_SERVICE_REGISTERED; - break; - case CALL_FORWARD_ACTION_ERASURE: - options.statusMessage = MMI_SM_KS_SERVICE_ERASED; - break; - } - } this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_QUERY_CALL_WAITING] = function REQUEST_QUERY_CALL_WAITING(length, options) { if (options.errorMsg) { - if (options.callback) { - // Prevent DataCloneError when sending chrome messages. - delete options.callback; - } - this.sendChromeMessage(options); return; } - if (options.callback) { - options.callback.call(this, options); - return; - } - let Buf = this.context.Buf; let results = Buf.readInt32List(); let enabled = (results[0] === 1); @@ -4945,37 +4381,23 @@ RilObject.prototype[REQUEST_QUERY_CALL_WAITING] = }; RilObject.prototype[REQUEST_SET_CALL_WAITING] = function REQUEST_SET_CALL_WAITING(length, options) { - if (options.errorMsg) { - if (options.callback) { - // Prevent DataCloneError when sending chrome messages. - delete options.callback; - } - - this.sendChromeMessage(options); - return; - } - - if (options.callback) { - options.callback.call(this, options); - return; - } - this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SMS_ACKNOWLEDGE] = null; RilObject.prototype[REQUEST_GET_IMEI] = function REQUEST_GET_IMEI(length, options) { this.IMEI = this.context.Buf.readString(); - let rilMessageType = options.rilMessageType; - // So far we only send the IMEI back to chrome if it was requested via MMI. - if (rilMessageType !== "sendMMI") { - return; - } - if (!options.errorMsg && this.IMEI == null) { - options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; + // If the request wasn't made by ril_worker itself, we send the IMEI back to + // chrome. + if (options.rilMessageType) { + if (options.errorMsg) { + this.sendChromeMessage(options); + return; + } + + options.imei = this.IMEI; + this.sendChromeMessage(options); } - options.statusMessage = this.IMEI; - this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_GET_IMEISV] = function REQUEST_GET_IMEISV(length, options) { if (options.errorMsg) { @@ -5003,57 +4425,22 @@ RilObject.prototype[REQUEST_QUERY_FACILITY_LOCK] = function REQUEST_QUERY_FACILI } // Buf.readInt32List()[0] for Call Barring is a bit vector of services. - let services = this.context.Buf.readInt32List()[0]; - + options.serviceClass = this.context.Buf.readInt32List()[0]; if (options.queryServiceClass) { - options.enabled = (services & options.queryServiceClass) ? true : false; + options.enabled = (options.serviceClass & options.queryServiceClass) ? true : false; options.serviceClass = options.queryServiceClass; } else { - options.enabled = services ? true : false; + options.enabled = options.serviceClass ? true : false; } - if (options.rilMessageType === "sendMMI") { - if (!options.enabled) { - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - } else { - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED_FOR; - let serviceClass = []; - for (let serviceClassMask = 1; - serviceClassMask <= ICC_SERVICE_CLASS_MAX; - serviceClassMask <<= 1) { - if ((serviceClassMask & services) !== 0) { - serviceClass.push(MMI_KS_SERVICE_CLASS_MAPPING[serviceClassMask]); - } - } - - options.additionalInformation = serviceClass; - } - } this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SET_FACILITY_LOCK] = function REQUEST_SET_FACILITY_LOCK(length, options) { options.retryCount = length ? this.context.Buf.readInt32List()[0] : -1; - - if (!options.errorMsg && (options.rilMessageType === "sendMMI")) { - switch (options.procedure) { - case MMI_PROCEDURE_ACTIVATION: - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED; - break; - case MMI_PROCEDURE_DEACTIVATION: - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - break; - } - } this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_CHANGE_BARRING_PASSWORD] = function REQUEST_CHANGE_BARRING_PASSWORD(length, options) { - if (options.rilMessageType != "sendMMI") { - this.sendChromeMessage(options); - return; - } - - options.statusMessage = MMI_SM_KS_PASSWORD_CHANGED; this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_QUERY_NETWORK_SELECTION_MODE] = function REQUEST_QUERY_NETWORK_SELECTION_MODE(length, options) { @@ -5129,25 +4516,7 @@ RilObject.prototype[REQUEST_QUERY_CLIP] = function REQUEST_QUERY_CLIP(length, op return; } - // options.provisioned informs about the called party receives the calling - // party's address information: - // 0 for CLIP not provisioned - // 1 for CLIP provisioned - // 2 for unknown options.provisioned = Buf.readInt32(); - if (options.rilMessageType === "sendMMI") { - switch (options.provisioned) { - case 0: - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - break; - case 1: - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED; - break; - default: - options.errorMsg = MMI_ERROR_KS_ERROR; - break; - } - } this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_LAST_DATA_CALL_FAIL_CAUSE] = null; @@ -5721,18 +5090,12 @@ RilObject.prototype[UNSOLICITED_ON_USSD] = function UNSOLICITED_ON_USSD() { this.context.debug("On USSD. Type Code: " + typeCode + " Message: " + message); } - let oldSession = this._ussdSession; - - // Per ril.h the USSD session is assumed to persist if the type code is "1". - this._ussdSession = typeCode == "1"; - - if (!oldSession && !this._ussdSession && !message) { - return; - } - this.sendChromeMessage({rilMessageType: "ussdreceived", message: message, - sessionEnded: !this._ussdSession}); + // Per ril.h the USSD session is assumed to persist if + // the type code is "1", otherwise the current session + // (if any) is assumed to have terminated. + sessionEnded: typeCode !== "1"}); }; RilObject.prototype[UNSOLICITED_ON_USSD_REQUEST] = null; RilObject.prototype[UNSOLICITED_NITZ_TIME_RECEIVED] = function UNSOLICITED_NITZ_TIME_RECEIVED() { diff --git a/dom/system/gonk/tests/test_ril_worker_mmi.js b/dom/system/gonk/tests/test_ril_worker_mmi.js deleted file mode 100644 index 80ecf228aebd..000000000000 --- a/dom/system/gonk/tests/test_ril_worker_mmi.js +++ /dev/null @@ -1,513 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this); - -function run_test() { - run_next_test(); -} - -function createMMIOptions(procedure, serviceCode, sia, sib, sic) { - let mmi = { - fullMMI: Array.slice(arguments).join("*") + "#", - procedure: procedure, - serviceCode: serviceCode, - sia: sia, - sib: sib, - sic: sic - }; - - return mmi; -} - -function testSendMMI(mmi, error) { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - do_print("worker.postMessage " + worker.postMessage); - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({rilMessageType: "sendMMI", mmi: mmi}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.rilMessageType, "sendMMI"); - equal(postedMessage.errorMsg, error); -} - -/** - * sendMMI tests. - */ - -add_test(function test_sendMMI_null() { - testSendMMI(null, MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_short_code() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - let ussdOptions; - - context.RIL.sendUSSD = function fakeSendUSSD(options){ - ussdOptions = options; - context.RIL[REQUEST_SEND_USSD](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: {fullMMI: "**"}}); - - let postedMessage = workerhelper.postedMessage; - equal(ussdOptions.ussd, "**"); - equal(postedMessage.errorMsg, undefined); - ok(context.RIL._ussdSession); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.changeICCPIN = function fakeChangeICCPIN(options) { - context.RIL[REQUEST_ENTER_SIM_PIN](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("**", "04", "1234", "4567", - "4567")}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, undefined); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN_no_new_PIN() { - testSendMMI(createMMIOptions("**", "04", "1234", "", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN_no_old_PIN() { - testSendMMI(createMMIOptions("**", "04", "", "1234", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN_wrong_procedure() { - testSendMMI(createMMIOptions("*", "04", "1234", "4567", "4567"), - MMI_ERROR_KS_INVALID_ACTION); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN_new_PIN_mismatch() { - testSendMMI(createMMIOptions("**", "04", "4567", "1234", "4567"), - MMI_ERROR_KS_MISMATCH_PIN); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN2() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.changeICCPIN2 = function fakeChangeICCPIN2(options) { - context.RIL[REQUEST_ENTER_SIM_PIN2](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("**", "042", "1234", "4567", - "4567")}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, undefined); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN2_no_new_PIN2() { - testSendMMI(createMMIOptions("**", "042", "1234", "", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN2_no_old_PIN2() { - testSendMMI(createMMIOptions("**", "042", "", "1234", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN2_wrong_procedure() { - testSendMMI(createMMIOptions("*", "042", "1234", "4567", "4567"), - MMI_ERROR_KS_INVALID_ACTION); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN2_new_PIN2_mismatch() { - testSendMMI(createMMIOptions("**", "042", "4567", "1234", "4567"), - MMI_ERROR_KS_MISMATCH_PIN); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.enterICCPUK = function fakeEnterICCPUK(options) { - context.RIL[REQUEST_ENTER_SIM_PUK](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("**", "05", "1234", "4567", - "4567")}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, undefined); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN_no_new_PIN() { - testSendMMI(createMMIOptions("**", "05", "1234", "", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN_no_PUK() { - testSendMMI(createMMIOptions("**", "05", "", "1234", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN_wrong_procedure() { - testSendMMI(createMMIOptions("*", "05", "1234", "4567", "4567"), - MMI_ERROR_KS_INVALID_ACTION); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN_new_PIN_mismatch() { - testSendMMI(createMMIOptions("**", "05", "4567", "1234", "4567"), - MMI_ERROR_KS_MISMATCH_PIN); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN2() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.enterICCPUK2 = function fakeEnterICCPUK2(options) { - context.RIL[REQUEST_ENTER_SIM_PUK2](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("**", "052", "1234", "4567", - "4567")}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, undefined); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN2_no_new_PIN2() { - testSendMMI(createMMIOptions("**", "052", "1234", "", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN2_no_PUK2() { - testSendMMI(createMMIOptions("**", "052", "", "1234", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN2_wrong_procedure() { - testSendMMI(createMMIOptions("*", "052", "1234", "4567", "4567"), - MMI_ERROR_KS_INVALID_ACTION); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN2_new_PIN_mismatch() { - testSendMMI(createMMIOptions("**", "052", "4567", "1234", "4567"), - MMI_ERROR_KS_MISMATCH_PIN); - - run_next_test(); -}); - -add_test(function test_sendMMI_get_IMEI() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let mmiOptions; - - context.RIL.getIMEI = function getIMEI(options) { - mmiOptions = options; - context.RIL[REQUEST_SEND_USSD](0, {}); - }; - - context.RIL.sendMMI({mmi: createMMIOptions("*#", "06")}); - - let postedMessage = workerhelper.postedMessage; - - notEqual(mmiOptions.mmi, null); - equal(postedMessage.errorMsg, undefined); - - run_next_test(); -}); - -add_test(function test_sendMMI_get_IMEI_error() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let mmiOptions; - - context.RIL.getIMEI = function getIMEI(options){ - mmiOptions = options; - context.RIL[REQUEST_SEND_USSD](0, { - errorMsg: GECKO_ERROR_RADIO_NOT_AVAILABLE - }); - }; - - context.RIL.sendMMI({mmi: createMMIOptions("*#", "06")}); - - let postedMessage = workerhelper.postedMessage; - - notEqual(mmiOptions.mmi, null); - equal (postedMessage.errorMsg, GECKO_ERROR_RADIO_NOT_AVAILABLE); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_barring_BAIC_interrogation_voice() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.Buf.readInt32List = function fakeReadUint32List() { - return [1]; - }; - - context.RIL.queryICCFacilityLock = - function fakeQueryICCFacilityLock(options) { - context.RIL[REQUEST_QUERY_FACILITY_LOCK](1, { - rilMessageType: "sendMMI" - }); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*#", "33")}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, undefined); - ok(postedMessage.enabled); - equal(postedMessage.statusMessage, MMI_SM_KS_SERVICE_ENABLED_FOR); - ok(Array.isArray(postedMessage.additionalInformation)); - equal(postedMessage.additionalInformation[0], "serviceClassVoice"); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_barring_BAIC_activation() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let mmiOptions; - - context.RIL.setICCFacilityLock = - function fakeSetICCFacilityLock(options) { - mmiOptions = options; - context.RIL[REQUEST_SET_FACILITY_LOCK](0, { - rilMessageType: "sendMMI", - procedure: MMI_PROCEDURE_ACTIVATION - }); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*", "33")}); - - let postedMessage = workerhelper.postedMessage; - - equal(mmiOptions.procedure, MMI_PROCEDURE_ACTIVATION); - equal(postedMessage.errorMsg, undefined); - equal(postedMessage.statusMessage, MMI_SM_KS_SERVICE_ENABLED); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_barring_BAIC_deactivation() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let mmiOptions; - - context.RIL.setICCFacilityLock = - function fakeSetICCFacilityLock(options) { - mmiOptions = options; - context.RIL[REQUEST_SET_FACILITY_LOCK](0, { - rilMessageType: "sendMMI", - procedure: MMI_PROCEDURE_DEACTIVATION - }); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("#", "33")}); - - let postedMessage = workerhelper.postedMessage; - - equal(mmiOptions.procedure, MMI_PROCEDURE_DEACTIVATION); - equal(postedMessage.errorMsg, undefined); - equal(postedMessage.statusMessage, MMI_SM_KS_SERVICE_DISABLED); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_barring_BAIC_procedure_not_supported() { - testSendMMI(createMMIOptions("**", "33", "0000"), MMI_ERROR_KS_NOT_SUPPORTED); - - run_next_test(); -}); - -add_test(function test_sendMMI_USSD() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let ussdOptions; - - context.RIL.sendUSSD = function fakeSendUSSD(options) { - ussdOptions = options; - context.RIL[REQUEST_SEND_USSD](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*", "123")}); - - let postedMessage = workerhelper.postedMessage; - - equal(ussdOptions.ussd, "**123#"); - equal(postedMessage.errorMsg, undefined); - ok(context.RIL._ussdSession); - - run_next_test(); -}); - -add_test(function test_sendMMI_USSD_error() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let ussdOptions; - - context.RIL.sendUSSD = function fakeSendUSSD(options){ - ussdOptions = options; - context.RIL[REQUEST_SEND_USSD](0, { - errorMsg: GECKO_ERROR_GENERIC_FAILURE - }); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*", "123")}); - - let postedMessage = workerhelper.postedMessage; - - equal(ussdOptions.ussd, "**123#"); - equal(postedMessage.errorMsg, GECKO_ERROR_GENERIC_FAILURE); - ok(!context.RIL._ussdSession); - - run_next_test(); -}); - -function setCallWaitingSuccess(mmi) { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.setCallWaiting = function fakeSetCallWaiting(options) { - context.RIL[REQUEST_SET_CALL_WAITING](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: mmi}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, undefined); -} - -add_test(function test_sendMMI_call_waiting_activation() { - setCallWaitingSuccess(createMMIOptions("*", "43", "10")); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_waiting_deactivation() { - setCallWaitingSuccess(createMMIOptions("#", "43")); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_waiting_registration() { - testSendMMI(createMMIOptions("**", "43"), MMI_ERROR_KS_NOT_SUPPORTED); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_waiting_erasure() { - testSendMMI(createMMIOptions("##", "43"), MMI_ERROR_KS_NOT_SUPPORTED); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_waiting_interrogation() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.Buf.readInt32 = function fakeReadUint32() { - return context.Buf.int32Array.pop(); - }; - - context.RIL.queryCallWaiting = function fakeQueryCallWaiting(options) { - context.Buf.int32Array = [ - 7, // serviceClass - 1, // enabled - 2 // length - ]; - context.RIL[REQUEST_QUERY_CALL_WAITING](1, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*#", "43")}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, undefined); - equal(postedMessage.serviceClass, 7); - run_next_test(); -}); diff --git a/dom/system/gonk/tests/test_ril_worker_mmi_cf.js b/dom/system/gonk/tests/test_ril_worker_mmi_cf.js deleted file mode 100644 index 1362b313ef0b..000000000000 --- a/dom/system/gonk/tests/test_ril_worker_mmi_cf.js +++ /dev/null @@ -1,156 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this); - -function run_test() { - run_next_test(); -} - -function createMMIOptions(procedure, serviceCode, sia, sib, sic) { - let mmi = { - fullMMI: Array.slice(arguments).join("*") + "#", - procedure: procedure, - serviceCode: serviceCode, - sia: sia, - sib: sib, - sic: sic - }; - - return mmi; -} - -function setCallForwardSuccess(procedure, serviceCode, sia, sib, sic) { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.setCallForward = function fakeSetCallForward(options) { - context.RIL[REQUEST_SET_CALL_FORWARD](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions(procedure, serviceCode, sia, sib, - sic)}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, undefined); -} - -add_test(function test_sendMMI_call_forwarding_activation() { - setCallForwardSuccess("*", "21", "12345", "99", "10"); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_forwarding_deactivation() { - setCallForwardSuccess("#", "21", "12345", "99", "10"); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_forwarding_interrogation() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.Buf.readInt32 = function fakeReadUint32() { - return context.Buf.int32Array.pop(); - }; - - context.Buf.readString = function fakeReadString() { - return "+34666222333"; - }; - - context.RIL.queryCallForwardStatus = function fakeQueryCallForward(options) { - context.Buf.int32Array = [ - 0, // rules.timeSeconds - 145, // rules.toa - 49, // rules.serviceClass - CALL_FORWARD_REASON_UNCONDITIONAL, // rules.reason - 1, // rules.active - 1 // rulesLength - ]; - context.RIL[REQUEST_QUERY_CALL_FORWARD_STATUS](1, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*#", "21")}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, undefined); - ok(Array.isArray(postedMessage.rules)); - equal(postedMessage.rules.length, 1); - ok(postedMessage.rules[0].active); - equal(postedMessage.rules[0].reason, CALL_FORWARD_REASON_UNCONDITIONAL); - equal(postedMessage.rules[0].number, "+34666222333"); - run_next_test(); -}); - -add_test(function test_sendMMI_call_forwarding_interrogation_no_rules() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.Buf.readInt32 = function fakeReadUint32() { - return 0; - }; - - context.RIL.queryCallForwardStatus = function fakeQueryCallForward(options) { - context.RIL[REQUEST_QUERY_CALL_FORWARD_STATUS](1, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*#", "21")}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, GECKO_ERROR_GENERIC_FAILURE); - - run_next_test(); -}); - - -add_test(function test_sendMMI_call_forwarding_registration() { - setCallForwardSuccess("**", "21", "12345", "99", "10"); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_forwarding_erasure() { - setCallForwardSuccess("##", "21", "12345", "99"); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_forwarding_CFB() { - setCallForwardSuccess("*", "67", "12345", "99", "10"); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_forwarding_CFNRy() { - setCallForwardSuccess("*", "61", "12345", "99", "10"); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_forwarding_CFNRc() { - setCallForwardSuccess("*", "62", "12345", "99", "10"); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_forwarding_CFAll() { - setCallForwardSuccess("*", "004", "12345", "99", "10"); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_forwarding_CFAllConditional() { - setCallForwardSuccess("*", "002", "12345", "99", "10"); - - run_next_test(); -}); diff --git a/dom/system/gonk/tests/xpcshell.ini b/dom/system/gonk/tests/xpcshell.ini index 97acee85db8c..6af3d7437f06 100644 --- a/dom/system/gonk/tests/xpcshell.ini +++ b/dom/system/gonk/tests/xpcshell.ini @@ -23,8 +23,6 @@ skip-if = true [test_ril_worker_sms_gsmpduhelper.js] [test_ril_worker_sms_segment_info.js] [test_ril_worker_smsc_address.js] -[test_ril_worker_mmi.js] -[test_ril_worker_mmi_cf.js] [test_ril_worker_cf.js] [test_ril_worker_cellbroadcast_config.js] [test_ril_worker_cellbroadcast.js] diff --git a/dom/telephony/gonk/TelephonyService.js b/dom/telephony/gonk/TelephonyService.js index bb3b6d9cf982..9b5b33ecc98d 100644 --- a/dom/telephony/gonk/TelephonyService.js +++ b/dom/telephony/gonk/TelephonyService.js @@ -23,8 +23,6 @@ const GONK_TELEPHONYSERVICE_CONTRACTID = const GONK_TELEPHONYSERVICE_CID = Components.ID("{67d26434-d063-4d28-9f48-5b3189788155}"); -const MOBILECALLFORWARDINGOPTIONS_CID = - Components.ID("{79b5988b-9436-48d8-a652-88fa033f146c}"); const TELEPHONYCALLINFO_CID = Components.ID("{d9e8b358-a02c-4cf3-9fc7-816c2e8d46e4}"); @@ -54,6 +52,173 @@ const DIAL_ERROR_RADIO_NOT_AVAILABLE = RIL.GECKO_ERROR_RADIO_NOT_AVAILABLE; const TONES_GAP_DURATION = 70; +// Consts for MMI. +// MMI procedure as defined in TS.22.030 6.5.2 +const MMI_PROCEDURE_ACTIVATION = "*"; +const MMI_PROCEDURE_DEACTIVATION = "#"; +const MMI_PROCEDURE_INTERROGATION = "*#"; +const MMI_PROCEDURE_REGISTRATION = "**"; +const MMI_PROCEDURE_ERASURE = "##"; + +// MMI call forwarding service codes as defined in TS.22.030 Annex B +const MMI_SC_CFU = "21"; +const MMI_SC_CF_BUSY = "67"; +const MMI_SC_CF_NO_REPLY = "61"; +const MMI_SC_CF_NOT_REACHABLE = "62"; +const MMI_SC_CF_ALL = "002"; +const MMI_SC_CF_ALL_CONDITIONAL = "004"; + +// MMI service codes for PIN/PIN2/PUK/PUK2 management as defined in TS.22.030 +// sec 6.6 +const MMI_SC_PIN = "04"; +const MMI_SC_PIN2 = "042"; +const MMI_SC_PUK = "05"; +const MMI_SC_PUK2 = "052"; + +// MMI service code for IMEI presentation as defined in TS.22.030 sec 6.7 +const MMI_SC_IMEI = "06"; + +// MMI call waiting service code +const MMI_SC_CALL_WAITING = "43"; + +// MMI service code for registration new password as defined in TS 22.030 6.5.4 +const MMI_SC_CHANGE_PASSWORD = "03"; +const MMI_ZZ_BARRING_SERVICE = "330"; + +// MMI call barring service codes +const MMI_SC_BAOC = "33"; +const MMI_SC_BAOIC = "331"; +const MMI_SC_BAOICxH = "332"; +const MMI_SC_BAIC = "35"; +const MMI_SC_BAICr = "351"; +const MMI_SC_BA_ALL = "330"; +const MMI_SC_BA_MO = "333"; +const MMI_SC_BA_MT = "353"; + +// MMI called line presentation service codes +const MMI_SC_CLIP = "30"; +const MMI_SC_CLIR = "31"; + +// MMI service code key strings. +const MMI_KS_SC_CALL_BARRING = "scCallBarring"; +const MMI_KS_SC_CALL_FORWARDING = "scCallForwarding"; +const MMI_KS_SC_CLIP = "scClip"; +const MMI_KS_SC_CLIR = "scClir"; +const MMI_KS_SC_PWD = "scPwd"; +const MMI_KS_SC_CALL_WAITING = "scCallWaiting"; +const MMI_KS_SC_PIN = "scPin"; +const MMI_KS_SC_PIN2 = "scPin2"; +const MMI_KS_SC_PUK = "scPuk"; +const MMI_KS_SC_PUK2 = "scPuk2"; +const MMI_KS_SC_CHANGE_PASSWORD = "scChangePassword"; +const MMI_KS_SC_IMEI = "scImei"; +const MMI_KS_SC_USSD = "scUssd"; +const MMI_KS_SC_CALL = "scCall"; + +// MMI error messages key strings. +const MMI_ERROR_KS_ERROR = "emMmiError"; +const MMI_ERROR_KS_NOT_SUPPORTED = "emMmiErrorNotSupported"; +const MMI_ERROR_KS_INVALID_ACTION = "emMmiErrorInvalidAction"; +const MMI_ERROR_KS_MISMATCH_PIN = "emMmiErrorMismatchPin"; +const MMI_ERROR_KS_MISMATCH_PASSWORD = "emMmiErrorMismatchPassword"; +const MMI_ERROR_KS_BAD_PIN = "emMmiErrorBadPin"; +const MMI_ERROR_KS_BAD_PUK = "emMmiErrorBadPuk"; +const MMI_ERROR_KS_INVALID_PIN = "emMmiErrorInvalidPin"; +const MMI_ERROR_KS_INVALID_PASSWORD = "emMmiErrorInvalidPassword"; +const MMI_ERROR_KS_NEEDS_PUK = "emMmiErrorNeedsPuk"; +const MMI_ERROR_KS_SIM_BLOCKED = "emMmiErrorSimBlocked"; + +// MMI status message. +const MMI_SM_KS_PASSWORD_CHANGED = "smPasswordChanged"; +const MMI_SM_KS_PIN_CHANGED = "smPinChanged"; +const MMI_SM_KS_PIN2_CHANGED = "smPin2Changed"; +const MMI_SM_KS_PIN_UNBLOCKED = "smPinUnblocked"; +const MMI_SM_KS_PIN2_UNBLOCKED = "smPin2Unblocked"; +const MMI_SM_KS_SERVICE_ENABLED = "smServiceEnabled"; +const MMI_SM_KS_SERVICE_ENABLED_FOR = "smServiceEnabledFor"; +const MMI_SM_KS_SERVICE_DISABLED = "smServiceDisabled"; +const MMI_SM_KS_SERVICE_REGISTERED = "smServiceRegistered"; +const MMI_SM_KS_SERVICE_ERASED = "smServiceErased"; +const MMI_SM_KS_SERVICE_INTERROGATED = "smServiceInterrogated"; +const MMI_SM_KS_SERVICE_NOT_PROVISIONED = "smServiceNotProvisioned"; +const MMI_SM_KS_CLIR_PERMANENT = "smClirPermanent"; +const MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_ON = "smClirDefaultOnNextCallOn"; +const MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_OFF = "smClirDefaultOnNextCallOff"; +const MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_ON = "smClirDefaultOffNextCallOn"; +const MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_OFF = "smClirDefaultOffNextCallOff"; +const MMI_SM_KS_CALL_CONTROL = "smCallControl"; + +// MMI Service class +const MMI_KS_SERVICE_CLASS_VOICE = "serviceClassVoice"; +const MMI_KS_SERVICE_CLASS_DATA = "serviceClassData"; +const MMI_KS_SERVICE_CLASS_FAX = "serviceClassFax"; +const MMI_KS_SERVICE_CLASS_SMS = "serviceClassSms"; +const MMI_KS_SERVICE_CLASS_DATA_SYNC = "serviceClassDataSync"; +const MMI_KS_SERVICE_CLASS_DATA_ASYNC = "serviceClassDataAsync"; +const MMI_KS_SERVICE_CLASS_PACKET = "serviceClassPacket"; +const MMI_KS_SERVICE_CLASS_PAD = "serviceClassPad"; + +const MMI_PROC_TO_CF_ACTION = {}; +MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_ACTIVATION] = Ci.nsIMobileConnection.CALL_FORWARD_ACTION_ENABLE; +MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_DEACTIVATION] = Ci.nsIMobileConnection.CALL_FORWARD_ACTION_DISABLE; +MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_INTERROGATION] = Ci.nsIMobileConnection.CALL_FORWARD_ACTION_QUERY_STATUS; +MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_REGISTRATION] = Ci.nsIMobileConnection.CALL_FORWARD_ACTION_REGISTRATION; +MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_ERASURE] = Ci.nsIMobileConnection.CALL_FORWARD_ACTION_ERASURE; + +const MMI_SC_TO_CF_REASON = {}; +MMI_SC_TO_CF_REASON[MMI_SC_CFU] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_UNCONDITIONAL; +MMI_SC_TO_CF_REASON[MMI_SC_CF_BUSY] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_MOBILE_BUSY; +MMI_SC_TO_CF_REASON[MMI_SC_CF_NO_REPLY] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_NO_REPLY; +MMI_SC_TO_CF_REASON[MMI_SC_CF_NOT_REACHABLE] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_NOT_REACHABLE; +MMI_SC_TO_CF_REASON[MMI_SC_CF_ALL] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_ALL_CALL_FORWARDING; +MMI_SC_TO_CF_REASON[MMI_SC_CF_ALL_CONDITIONAL] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING; + +const MMI_SC_TO_LOCK_TYPE = {}; +MMI_SC_TO_LOCK_TYPE[MMI_SC_PIN] = Ci.nsIIcc.CARD_LOCK_TYPE_PIN; +MMI_SC_TO_LOCK_TYPE[MMI_SC_PIN2] = Ci.nsIIcc.CARD_LOCK_TYPE_PIN2; +MMI_SC_TO_LOCK_TYPE[MMI_SC_PUK] = Ci.nsIIcc.CARD_LOCK_TYPE_PUK; +MMI_SC_TO_LOCK_TYPE[MMI_SC_PUK2] = Ci.nsIIcc.CARD_LOCK_TYPE_PUK2; + +const MMI_PROC_TO_CLIR_ACTION = {}; +MMI_PROC_TO_CLIR_ACTION[MMI_PROCEDURE_ACTIVATION] = Ci.nsIMobileConnection.CLIR_INVOCATION; +MMI_PROC_TO_CLIR_ACTION[MMI_PROCEDURE_DEACTIVATION] = Ci.nsIMobileConnection.CLIR_SUPPRESSION; + +const MMI_SC_TO_CB_PROGRAM = {}; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BAOC] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BAOIC] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BAOICxH] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL_EXCEPT_HOME; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BAIC] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_ALL_INCOMING; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BAICr] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_INCOMING_ROAMING; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BA_ALL] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_ALL_SERVICE; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BA_MO] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_OUTGOING_SERVICE; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BA_MT] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_INCOMING_SERVICE; + +const CF_ACTION_TO_STATUS_MESSAGE = {}; +CF_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CALL_FORWARD_ACTION_ENABLE] = MMI_SM_KS_SERVICE_ENABLED; +CF_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CALL_FORWARD_ACTION_DISABLE] = MMI_SM_KS_SERVICE_DISABLED; +CF_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CALL_FORWARD_ACTION_REGISTRATION] = MMI_SM_KS_SERVICE_REGISTERED; +CF_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CALL_FORWARD_ACTION_ERASURE] = MMI_SM_KS_SERVICE_ERASED; + +const LOCK_TYPE_TO_STATUS_MESSAGE = {}; +LOCK_TYPE_TO_STATUS_MESSAGE[Ci.nsIIcc.CARD_LOCK_TYPE_PIN] = MMI_SM_KS_PIN_CHANGED; +LOCK_TYPE_TO_STATUS_MESSAGE[Ci.nsIIcc.CARD_LOCK_TYPE_PIN2] = MMI_SM_KS_PIN2_CHANGED; +LOCK_TYPE_TO_STATUS_MESSAGE[Ci.nsIIcc.CARD_LOCK_TYPE_PUK] = MMI_SM_KS_PIN_UNBLOCKED; +LOCK_TYPE_TO_STATUS_MESSAGE[Ci.nsIIcc.CARD_LOCK_TYPE_PUK2] = MMI_SM_KS_PIN2_UNBLOCKED; + +const CLIR_ACTION_TO_STATUS_MESSAGE = {}; +CLIR_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CLIR_INVOCATION] = MMI_SM_KS_SERVICE_ENABLED; +CLIR_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CLIR_SUPPRESSION] = MMI_SM_KS_SERVICE_DISABLED; + +const MMI_KS_SERVICE_CLASS_MAPPING = {}; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE] = MMI_KS_SERVICE_CLASS_VOICE; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA] = MMI_KS_SERVICE_CLASS_DATA; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_FAX] = MMI_KS_SERVICE_CLASS_FAX; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_SMS] = MMI_KS_SERVICE_CLASS_SMS; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_SYNC] = MMI_KS_SERVICE_CLASS_DATA_SYNC; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_ASYNC] = MMI_KS_SERVICE_CLASS_DATA_ASYNC; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_PACKET] = MMI_KS_SERVICE_CLASS_PACKET; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_PAD] = MMI_KS_SERVICE_CLASS_PAD; + let DEBUG; function debug(s) { dump("TelephonyService: " + s + "\n"); @@ -88,6 +253,12 @@ XPCOMUtils.defineLazyServiceGetter(this, "gGonkMobileConnectionService", "@mozilla.org/mobileconnection/mobileconnectionservice;1", "nsIGonkMobileConnectionService"); +/* global gIccService */ +XPCOMUtils.defineLazyServiceGetter(this, "gIccService", + "@mozilla.org/icc/iccservice;1", + "nsIIccService"); + + /* global PhoneNumberUtils */ XPCOMUtils.defineLazyModuleGetter(this, "PhoneNumberUtils", "resource://gre/modules/PhoneNumberUtils.jsm"); @@ -96,30 +267,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PhoneNumberUtils", XPCOMUtils.defineLazyModuleGetter(this, "DialNumberUtils", "resource://gre/modules/DialNumberUtils.jsm"); -function MobileCallForwardingOptions(aOptions) { - for (let key in aOptions) { - this[key] = aOptions[key]; - } -} -MobileCallForwardingOptions.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileCallForwardingOptions]), - classID: MOBILECALLFORWARDINGOPTIONS_CID, - classInfo: XPCOMUtils.generateCI({ - classID: MOBILECALLFORWARDINGOPTIONS_CID, - classDescription: "MobileCallForwardingOptions", - interfaces: [Ci.nsIMobileCallForwardingOptions] - }), - - // nsIMobileForwardingOptions - - active: false, - action: nsIMobileConnection.CALL_FORWARD_ACTION_UNKNOWN, - reason: nsIMobileConnection.CALL_FORWARD_REASON_UNKNOWN, - number: null, - timeSeconds: -1, - serviceClass: nsIMobileConnection.ICC_SERVICE_CLASS_NONE -}; - function TelephonyCallInfo(aCall) { this.clientId = aCall.clientId; this.callIndex = aCall.callIndex; @@ -213,6 +360,7 @@ function TelephonyService() { this._currentCalls = {}; this._currentConferenceState = nsITelephonyService.CALL_STATE_UNKNOWN; this._audioStates = []; + this._ussdSessions = []; this._cdmaCallWaitingNumber = null; @@ -226,6 +374,7 @@ function TelephonyService() { for (let i = 0; i < this._numClients; ++i) { this._audioStates[i] = nsITelephonyAudioService.PHONE_STATE_NORMAL; + this._ussdSessions[i] = false; this._currentCalls[i] = {}; this._enumerateCallsForClient(i); } @@ -248,6 +397,15 @@ TelephonyService.prototype = { _callRingWakeLock: null, _callRingWakeLockTimer: null, + /** + * USSD session flags. + * Only one USSD session may exist at a time, and the session is assumed + * to exist until: + * a) There's a call to cancelUSSD() + * b) Receiving a session end unsolicited event. + */ + _ussdSessions: null, + _acquireCallRingWakeLock: function() { if (!this._callRingWakeLock) { if (DEBUG) debug("Acquiring a CPU wake lock for handling incoming call."); @@ -403,10 +561,6 @@ TelephonyService.prototype = { } }, - _rulesToCallForwardingOptions: function(aRules) { - return aRules.map(rule => new MobileCallForwardingOptions(rule)); - }, - _updateDebugFlag: function() { try { DEBUG = RIL.DEBUG_RIL || @@ -451,7 +605,7 @@ TelephonyService.prototype = { * MMI full object. */ _isTemporaryCLIR: function(aMmi) { - return (aMmi && aMmi.serviceCode === RIL.MMI_SC_CLIR) && aMmi.dialNumber; + return (aMmi && aMmi.serviceCode === MMI_SC_CLIR) && aMmi.dialNumber; }, /** @@ -464,12 +618,12 @@ TelephonyService.prototype = { // In temporary mode, MMI_PROCEDURE_ACTIVATION means allowing CLI // presentation, i.e. CLIR_SUPPRESSION. See TS 22.030, Annex B. switch (aProcedure) { - case RIL.MMI_PROCEDURE_ACTIVATION: - return RIL.CLIR_SUPPRESSION; - case RIL.MMI_PROCEDURE_DEACTIVATION: - return RIL.CLIR_INVOCATION; + case MMI_PROCEDURE_ACTIVATION: + return Ci.nsIMobileConnection.CLIR_SUPPRESSION; + case MMI_PROCEDURE_DEACTIVATION: + return Ci.nsIMobileConnection.CLIR_INVOCATION; default: - return RIL.CLIR_DEFAULT; + return Ci.nsIMobileConnection.CLIR_DEFAULT; } }, @@ -633,27 +787,27 @@ TelephonyService.prototype = { // Handling of supplementary services within a call as 3GPP TS 22.030 6.5.5 _dialInCallMMI: function(aClientId, aNumber, aCallback) { let mmiCallback = { - notifyError: () => aCallback.notifyDialMMIError(RIL.MMI_ERROR_KS_ERROR), - notifySuccess: () => aCallback.notifyDialMMISuccess(RIL.MMI_SM_KS_CALL_CONTROL) + notifyError: () => aCallback.notifyDialMMIError(MMI_ERROR_KS_ERROR), + notifySuccess: () => aCallback.notifyDialMMISuccess(MMI_SM_KS_CALL_CONTROL) }; if (aNumber === "0") { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this._hangUpBackground(aClientId, mmiCallback); } else if (aNumber === "1") { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this._hangUpForeground(aClientId, mmiCallback); } else if (aNumber[0] === "1" && aNumber.length === 2) { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this.hangUpCall(aClientId, parseInt(aNumber[1]), mmiCallback); } else if (aNumber === "2") { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this._switchActiveCall(aClientId, mmiCallback); } else if (aNumber[0] === "2" && aNumber.length === 2) { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this._separateCallGsm(aClientId, parseInt(aNumber[1]), mmiCallback); } else if (aNumber === "3") { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this._conferenceCallGsm(aClientId, mmiCallback); } else { this._dialCall(aClientId, aNumber, undefined, aCallback); @@ -844,126 +998,670 @@ TelephonyService.prototype = { * Parsed MMI structure. * @param aCallback * A nsITelephonyDialCallback object. - * @param aStartNewSession - * True to start a new session for ussd request. */ _dialMMI: function(aClientId, aMmi, aCallback) { let mmiServiceCode = aMmi ? - this._serviceCodeToKeyString(aMmi.serviceCode) : RIL.MMI_KS_SC_USSD; + this._serviceCodeToKeyString(aMmi.serviceCode) : MMI_KS_SC_USSD; aCallback.notifyDialMMI(mmiServiceCode); - this._sendToRilWorker(aClientId, "sendMMI", - { mmi: aMmi }, response => { - if (DEBUG) debug("MMI response: " + JSON.stringify(response)); + if (mmiServiceCode !== RIL.MMI_KS_SC_IMEI && !this._isRadioOn(aClientId)) { + aCallback.notifyDialMMIError(DIAL_ERROR_RADIO_NOT_AVAILABLE); + return; + } - if (response.errorMsg) { - if (response.additionalInformation != null) { - aCallback.notifyDialMMIErrorWithInfo(response.errorMsg, - response.additionalInformation); - } else { - aCallback.notifyDialMMIError(response.errorMsg); + // We check if the MMI service code is supported and in that case we + // trigger the appropriate RIL request if possible. + switch (mmiServiceCode) { + // Call Forwarding + case MMI_KS_SC_CALL_FORWARDING: + this._callForwardingMMI(aClientId, aMmi, aCallback); + break; + + // Change the current ICC PIN number. + case MMI_KS_SC_PIN: + // Change the current ICC PIN2 number. + case MMI_KS_SC_PIN2: + this._iccChangeLockMMI(aClientId, aMmi, aCallback); + break; + + // Unblock ICC PUK. + case MMI_KS_SC_PUK: + // Unblock ICC PUN2. + case MMI_KS_SC_PUK2: + this._iccUnlockMMI(aClientId, aMmi, aCallback); + break; + + // IMEI + case MMI_KS_SC_IMEI: + this._getImeiMMI(aClientId, aMmi, aCallback); + break; + + // CLIP + case MMI_KS_SC_CLIP: + this._clipMMI(aClientId, aMmi, aCallback); + break; + + // CLIR (non-temporary ones) + case MMI_KS_SC_CLIR: + this._clirMMI(aClientId, aMmi, aCallback); + break; + + // Change call barring password + case MMI_KS_SC_CHANGE_PASSWORD: + this._callBarringPasswordMMI(aClientId, aMmi, aCallback); + break; + + // Call barring + case MMI_KS_SC_CALL_BARRING: + this._callBarringMMI(aClientId, aMmi, aCallback); + break; + + // Call waiting + case MMI_KS_SC_CALL_WAITING: + this._callWaitingMMI(aClientId, aMmi, aCallback); + break; + + // Handle unknown MMI code as USSD. + default: + this._sendUSSDInternal(aClientId, aMmi.fullMMI, aResponse => { + if (aResponse.errorMsg) { + aCallback.notifyDialMMIError(aResponse.errorMsg); + return; + } + + aCallback.notifyDialMMISuccess(""); + }); + break; + } + }, + + /** + * Handle call forwarding MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _callForwardingMMI: function(aClientId, aMmi, aCallback) { + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + let action = MMI_PROC_TO_CF_ACTION[aMmi.procedure]; + let reason = MMI_SC_TO_CF_REASON[aMmi.serviceCode]; + + if (action === Ci.nsIMobileConnection.CALL_FORWARD_ACTION_QUERY_STATUS) { + connection.getCallForwarding(reason, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifyGetCallForwardingSuccess: function(aCount, aResults) { + aCallback.notifyDialMMISuccessWithCallForwardingOptions( + MMI_SM_KS_SERVICE_INTERROGATED, aCount, aResults); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + } else { + let number = aMmi.sia; + let serviceClass = this._siToServiceClass(aMmi.sib); + let timeSeconds = aMmi.sic; + connection.setCallForwarding(action, reason, number, timeSeconds, + serviceClass, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess(CF_ACTION_TO_STATUS_MESSAGE[action]); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + } + }, + + /** + * Handle icc change lock MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _iccChangeLockMMI: function(aClientId, aMmi, aCallback) { + let errorMsg = this._getIccLockMMIError(aMmi); + if (errorMsg) { + aCallback.notifyDialMMIError(errorMsg); + return; + } + + let icc = gIccService.getIccByServiceId(aClientId); + let lockType = MMI_SC_TO_LOCK_TYPE[aMmi.serviceCode]; + + icc.changeCardLockPassword(lockType, aMmi.sia, aMmi.sib, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess(LOCK_TYPE_TO_STATUS_MESSAGE[lockType]); + }, + notifyCardLockError: function(aErrorMsg, aRetryCount) { + if (aRetryCount <= 0) { + if (lockType === Ci.nsIIcc.CARD_LOCK_TYPE_PIN) { + aErrorMsg = MMI_ERROR_KS_NEEDS_PUK; + } + + aCallback.notifyDialMMIError(aErrorMsg); + return; } + + aCallback.notifyDialMMIErrorWithInfo(MMI_ERROR_KS_BAD_PIN, + aRetryCount); + }, + }); + }, + + /** + * Handle icc unlock lock MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _iccUnlockMMI: function(aClientId, aMmi, aCallback) { + let errorMsg = this._getIccLockMMIError(aMmi); + if (errorMsg) { + aCallback.notifyDialMMIError(errorMsg); + return; + } + + let icc = gIccService.getIccByServiceId(aClientId); + let lockType = MMI_SC_TO_LOCK_TYPE[aMmi.serviceCode]; + + icc.unlockCardLock(lockType, aMmi.sia, aMmi.sib, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess(LOCK_TYPE_TO_STATUS_MESSAGE[lockType]); + }, + notifyCardLockError: function(aErrorMsg, aRetryCount) { + if (aRetryCount <= 0) { + if (lockType === Ci.nsIIcc.CARD_LOCK_TYPE_PUK) { + aErrorMsg = MMI_ERROR_KS_SIM_BLOCKED; + } + + aCallback.notifyDialMMIError(aErrorMsg); + return; + } + + aCallback.notifyDialMMIErrorWithInfo(MMI_ERROR_KS_BAD_PUK, + aRetryCount); + }, + }); + }, + + /** + * Handle IMEI MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _getImeiMMI: function(aClientId, aMmi, aCallback) { + this._sendToRilWorker(aClientId, "getIMEI", {}, aResponse => { + if (aResponse.errorMsg) { + aCallback.notifyDialMMIError(aResponse.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) { + // without containing an actual IMEI number is considered an error. + if (!aResponse.imei) { 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) { - let callForwardingOptions = - this._rulesToCallForwardingOptions(response.additionalInformation); - aCallback.notifyDialMMISuccessWithCallForwardingOptions( - response.statusMessage, callForwardingOptions.length, callForwardingOptions); - return; - } - } - - // No additional information - if (response.additionalInformation === undefined) { - aCallback.notifyDialMMISuccess(response.statusMessage); - return; - } - - // Additional information is an integer. - if (!isNaN(parseInt(response.additionalInformation, 10))) { - aCallback.notifyDialMMISuccessWithInteger( - response.statusMessage, response.additionalInformation); - return; - } - - // Additional information is an array of strings. - let array = response.additionalInformation; - if (Array.isArray(array) && array.length > 0 && typeof array[0] === "string") { - aCallback.notifyDialMMISuccessWithStrings(response.statusMessage, - array.length, array); - return; - } - - aCallback.notifyDialMMISuccess(response.statusMessage); + aCallback.notifyDialMMISuccess(aResponse.imei); }); }, + /** + * Handle CLIP MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _clipMMI: function(aClientId, aMmi, aCallback) { + if (aMmi.procedure !== MMI_PROCEDURE_INTERROGATION) { + aCallback.notifyDialMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + return; + } + + this._sendToRilWorker(aClientId, "queryCLIP", {}, aResponse => { + if (aResponse.errorMsg) { + aCallback.notifyDialMMIError(aResponse.errorMsg); + return; + } + + // aResponse.provisioned informs about the called party receives the + // calling party's address information: + // 0 for CLIP not provisioned + // 1 for CLIP provisioned + // 2 for unknown + switch (aResponse.provisioned) { + case 0: + aCallback.notifyDialMMISuccess(MMI_SM_KS_SERVICE_DISABLED); + break; + case 1: + aCallback.notifyDialMMISuccess(MMI_SM_KS_SERVICE_ENABLED); + break; + default: + aCallback.notifyDialMMIError(MMI_ERROR_KS_ERROR); + break; + } + }); + }, + + /** + * Handle CLIR MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _clirMMI: function(aClientId, aMmi, aCallback) { + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + switch (aMmi.procedure) { + case MMI_PROCEDURE_INTERROGATION: + connection.getCallingLineIdRestriction({ + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifyGetClirStatusSuccess: function(aN, aM) { + let errorMsg; + let statusMessage; + // TS 27.007 +CLIR parameter 'm'. + switch (aM) { + // CLIR not provisioned. + case 0: + statusMessage = MMI_SM_KS_SERVICE_NOT_PROVISIONED; + break; + // CLIR provisioned in permanent mode. + case 1: + statusMessage = MMI_SM_KS_CLIR_PERMANENT; + break; + // Unknown (e.g. no network, etc.). + case 2: + errorMsg = MMI_ERROR_KS_ERROR; + break; + // CLIR temporary mode presentation restricted. + case 3: + // TS 27.007 +CLIR parameter 'n'. + switch (aN) { + // Default. + case 0: + // CLIR invocation. + case 1: + statusMessage = MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_ON; + break; + // CLIR suppression. + case 2: + statusMessage = MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_OFF; + break; + default: + errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE; + break; + } + break; + // CLIR temporary mode presentation allowed. + case 4: + // TS 27.007 +CLIR parameter 'n'. + switch (aN) { + // Default. + case 0: + // CLIR suppression. + case 2: + statusMessage = MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_OFF; + break; + // CLIR invocation. + case 1: + statusMessage = MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_ON; + break; + default: + errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE; + break; + } + break; + default: + errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE; + break; + } + + if (errorMsg) { + aCallback.notifyDialMMIError(errorMsg); + return; + } + + aCallback.notifyDialMMISuccess(statusMessage); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + case MMI_PROCEDURE_ACTIVATION: + case MMI_PROCEDURE_DEACTIVATION: { + let clirMode = MMI_PROC_TO_CLIR_ACTION[aMmi.procedure]; + connection.setCallingLineIdRestriction(clirMode, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess(CLIR_ACTION_TO_STATUS_MESSAGE[clirMode]); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + } + default: + aCallback.notifyDialMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + break; + } + }, + + /** + * Handle change call barring password MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _callBarringPasswordMMI: function(aClientId, aMmi, aCallback) { + if (aMmi.procedure !== MMI_PROCEDURE_REGISTRATION && + aMmi.procedure !== MMI_PROCEDURE_ACTIVATION) { + aCallback.notifyDialMMIError(MMI_ERROR_KS_INVALID_ACTION); + return; + } + + if (aMmi.sia !== "" && aMmi.sia !== MMI_ZZ_BARRING_SERVICE) { + aCallback.notifyDialMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + return; + } + + let validPassword = aSi => /^[0-9]{4}$/.test(aSi); + if (!validPassword(aMmi.sib) || !validPassword(aMmi.sic) || + !validPassword(aMmi.pwd)) { + aCallback.notifyDialMMIError(MMI_ERROR_KS_INVALID_PASSWORD); + return; + } + + if (aMmi.sic !== aMmi.pwd) { + aCallback.notifyDialMMIError(MMI_ERROR_KS_MISMATCH_PASSWORD); + return; + } + + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + connection.changeCallBarringPassword(aMmi.sib, aMmi.sic, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess(MMI_SM_KS_PASSWORD_CHANGED); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + }, + + /** + * Handle call barring MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _callBarringMMI: function(aClientId, aMmi, aCallback) { + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + let program = MMI_SC_TO_CB_PROGRAM[aMmi.serviceCode]; + let password = aMmi.sia || ""; + let serviceClass = this._siToServiceClass(aMmi.sib); + + switch (aMmi.procedure) { + case MMI_PROCEDURE_INTERROGATION: + connection.getCallBarring(program, password, serviceClass, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifyGetCallBarringSuccess: function(aProgram, aEnabled, aServiceClass) { + if (!aEnabled) { + aCallback.notifyDialMMISuccess(MMI_SM_KS_SERVICE_DISABLED); + return; + } + + let services = this._serviceClassToStringArray(aServiceClass); + aCallback.notifyDialMMISuccessWithStrings(MMI_SM_KS_SERVICE_ENABLED_FOR, + services.length, services); + }.bind(this), + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + case MMI_PROCEDURE_ACTIVATION: + case MMI_PROCEDURE_DEACTIVATION: { + let enabled = (aMmi.procedure === MMI_PROCEDURE_ACTIVATION); + connection.setCallBarring(program, enabled, password, serviceClass, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess( + enabled ? MMI_SM_KS_SERVICE_ENABLED + : MMI_SM_KS_SERVICE_DISABLED + ); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + } + default: + aCallback.notifyDialMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + break; + } + }, + + /** + * Handle call waiting MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _callWaitingMMI: function(aClientId, aMmi, aCallback) { + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + + switch (aMmi.procedure) { + case MMI_PROCEDURE_INTERROGATION: + connection.getCallWaiting({ + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifyGetCallWaitingSuccess: function(aServiceClass) { + if (aServiceClass === Ci.nsIMobileConnection.ICC_SERVICE_CLASS_NONE) { + aCallback.notifyDialMMISuccess(MMI_SM_KS_SERVICE_DISABLED); + return; + } + + let services = this._serviceClassToStringArray(aServiceClass); + aCallback.notifyDialMMISuccessWithStrings(MMI_SM_KS_SERVICE_ENABLED_FOR, + services.length, services); + }.bind(this), + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + case MMI_PROCEDURE_ACTIVATION: + case MMI_PROCEDURE_DEACTIVATION: { + let enabled = (aMmi.procedure === MMI_PROCEDURE_ACTIVATION); + let serviceClass = this._siToServiceClass(aMmi.sia); + connection.setCallWaiting(enabled, serviceClass, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess( + enabled ? MMI_SM_KS_SERVICE_ENABLED + : MMI_SM_KS_SERVICE_DISABLED + ); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + } + default: + aCallback.notifyDialMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + break; + } + }, + _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; - case RIL.MMI_SC_CHANGE_PASSWORD: - return RIL.MMI_KS_SC_CHANGE_PASSWORD; + 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; + case MMI_SC_CHANGE_PASSWORD: + return MMI_KS_SC_CHANGE_PASSWORD; default: - return RIL.MMI_KS_SC_USSD; + return MMI_KS_SC_USSD; } }, + /** + * Helper for translating basic service group to service class parameter. + */ + _siToServiceClass: function(aSi) { + if (!aSi) { + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_NONE; + } + + let serviceCode = parseInt(aSi, 10); + switch (serviceCode) { + case 10: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_SMS + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_FAX + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE; + case 11: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE; + case 12: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_SMS + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_FAX; + case 13: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_FAX; + case 16: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_SMS; + case 19: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_FAX + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE; + case 21: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_PAD + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_ASYNC; + case 22: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_PACKET + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_SYNC; + case 25: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_ASYNC; + case 26: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_SYNC + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE; + case 99: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_PACKET; + default: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_NONE; + } + }, + + _serviceClassToStringArray: function(aServiceClass) { + let services = []; + for (let mask = Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE; + mask <= Ci.nsIMobileConnection.ICC_SERVICE_CLASS_MAX; + mask <<= 1) { + if (mask & aServiceClass) { + services.push(MMI_KS_SERVICE_CLASS_MAPPING[mask]); + } + } + return services; + }, + + _getIccLockMMIError: function(aMmi) { + // As defined in TS.122.030 6.6.2 to change the ICC PIN we should expect + // an MMI code of the form **04*OLD_PIN*NEW_PIN*NEW_PIN#, where old PIN + // should be entered as the SIA parameter and the new PIN as SIB and + // SIC. + if (aMmi.procedure !== MMI_PROCEDURE_REGISTRATION) { + return MMI_ERROR_KS_INVALID_ACTION; + } + + if (!aMmi.sia || !aMmi.sib || !aMmi.sic) { + return MMI_ERROR_KS_ERROR; + } + + if (aMmi.sia.length < 4 || aMmi.sia.length > 8 || + aMmi.sib.length < 4 || aMmi.sib.length > 8 || + aMmi.sic.length < 4 || aMmi.sic.length > 8) { + return MMI_ERROR_KS_INVALID_PIN; + } + + if (aMmi.sib != aMmi.sic) { + return MMI_ERROR_KS_MISMATCH_PIN; + } + + return null; + }, + /** * The default callback handler for call operations. * @@ -1405,13 +2103,42 @@ TelephonyService.prototype = { }, sendUSSD: function(aClientId, aUssd, aCallback) { - this._sendToRilWorker(aClientId, "sendUSSD", { ussd: aUssd }, - this._defaultCallbackHandler.bind(this, aCallback)); + this._sendUSSDInternal(aClientId, aUssd, + this._defaultCallbackHandler.bind(this, aCallback)); + }, + + _sendUSSDInternal: function(aClientId, aUssd, aCallback) { + if (!this._ussdSessions[aClientId]) { + this._sendToRilWorker(aClientId, "sendUSSD", { ussd: aUssd }, aResponse => { + this._ussdSessions[aClientId] = !aResponse.errorMsg; + aCallback(aResponse); + }); + return; + } + + // Cancel the previous ussd session first. + this._cancelUSSDInternal(aClientId, aResponse => { + // Fail to cancel ussd session, report error instead of sending ussd + // request. + if (aResponse.errorMsg) { + aCallback(aResponse); + return; + } + + this._sendUSSDInternal(aClientId, aUssd, aCallback); + }); }, cancelUSSD: function(aClientId, aCallback) { - this._sendToRilWorker(aClientId, "cancelUSSD", {}, - this._defaultCallbackHandler.bind(this, aCallback)); + this._cancelUSSDInternal(aClientId, + this._defaultCallbackHandler.bind(this, aCallback)); + }, + + _cancelUSSDInternal: function(aClientId, aCallback) { + this._sendToRilWorker(aClientId, "cancelUSSD", {}, aResponse => { + this._ussdSessions[aClientId] = !!aResponse.errorMsg; + aCallback(aResponse); + }); }, get microphoneMuted() { @@ -1679,6 +2406,13 @@ TelephonyService.prototype = { aMessage + " (sessionEnded : " + aSessionEnded + ")"); } + let oldSession = this._ussdSessions[aClientId]; + this._ussdSessions[aClientId] = !aSessionEnded; + + if (!oldSession && !this._ussdSessions[aClientId] && !aMessage) { + return; + } + gTelephonyMessenger.notifyUssdReceived(aClientId, aMessage, aSessionEnded); }, diff --git a/dom/telephony/test/marionette/manifest.ini b/dom/telephony/test/marionette/manifest.ini index 028cde75dbbb..a3b16ccddd23 100644 --- a/dom/telephony/test/marionette/manifest.ini +++ b/dom/telephony/test/marionette/manifest.ini @@ -33,11 +33,18 @@ qemu = true [test_incoming_answer_hangup_oncallschanged.js] [test_incoming_basic_operations.js] [test_incoming_onstatechange.js] -[test_mmi.js] +[test_mmi_call_barring.js] [test_mmi_call_forwarding.js] +[test_mmi_call_waiting.js] [test_mmi_change_barring_password.js] [test_mmi_change_pin.js] +[test_mmi_change_pin2.js] +[test_mmi_clip.js] +[test_mmi_clir.js] +[test_mmi_imei.js] [test_mmi_unlock_puk.js] +[test_mmi_unlock_puk2.js] +[test_mmi_ussd.js] [test_multiple_hold.js] [test_outgoing_already_held.js] [test_outgoing_answer_hangup_oncallschanged.js] diff --git a/dom/telephony/test/marionette/test_mmi_call_barring.js b/dom/telephony/test/marionette/test_mmi_call_barring.js new file mode 100644 index 000000000000..742204cfe69f --- /dev/null +++ b/dom/telephony/test/marionette/test_mmi_call_barring.js @@ -0,0 +1,98 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +// Password is hardcoded as "0000" by default in emulator. +const PASSWORD = "0000"; +// Emulator doesn't support BA_ALL(330), BA_MO(333), BA_MT(353). +const CB_TYPES = ["33", "331", "332", "35", "351"]; +const CB_TYPES_UNSUPPORTED = ["330", "333", "353"]; +// Basic Service - 10: Voice + Fax + SMS. +const BS = "10"; +const MMI_SERVICE_CLASS = ["serviceClassVoice", "serviceClassFax", + "serviceClassSms"]; +// Call barring doesn't support Registration (**) and Erasure (##) operation. +const OPERATION_UNSUPPORTED = ["**", "##"]; + +function sendCbMMI(aOperation, aType, aExpectedSuccess, aExpectedStatusMessage) { + let mmi = aOperation + aType + "*" + PASSWORD + "*" + BS + "#"; + log("Test " + mmi + " ..."); + + return gSendMMI(mmi) + .then((aResult) => { + is(aResult.success, aExpectedSuccess, "Check success"); + is(aResult.serviceCode, "scCallBarring", "Check serviceCode"); + is(aResult.statusMessage, aExpectedStatusMessage, "Check statusMessage"); + return aResult; + }); +} + +function testCallBarring(aEnabled) { + let promise = Promise.resolve(); + + CB_TYPES.forEach(function(aType) { + promise = promise + // Test setting call barring. + .then(() => sendCbMMI(aEnabled ? "*" : "#", aType, true, + aEnabled ? "smServiceEnabled" : "smServiceDisabled")) + // Test getting call barring. + .then(() => sendCbMMI("*#", aType, true, + aEnabled ? "smServiceEnabledFor": "smServiceDisabled")) + .then(aResult => { + if (aEnabled) { + is(aResult.additionalInformation.length, MMI_SERVICE_CLASS.length, + "Check additionalInformation.length"); + for (let i = 0; i < MMI_SERVICE_CLASS.length; i++) { + is(aResult.additionalInformation[i], MMI_SERVICE_CLASS[i], + "Check additionalInformation[" + i + "]"); + } + } + }); + }); + + return promise; +} + +function testUnsupportType() { + let promise = Promise.resolve(); + + CB_TYPES_UNSUPPORTED.forEach(function(aType) { + promise = promise + // Test setting call barring. + .then(() => sendCbMMI("*", aType, false, "RequestNotSupported")) + // Test getting call barring. + .then(() => sendCbMMI("*#", aType, false, "RequestNotSupported")); + }); + + return promise; +} + +function testUnsupportedOperation() { + let promise = Promise.resolve(); + + let types = CB_TYPES.concat(CB_TYPES_UNSUPPORTED); + types.forEach(function(aType) { + OPERATION_UNSUPPORTED.forEach(function(aOperation) { + promise = promise + .then(() => sendCbMMI(aOperation, aType, false, + "emMmiErrorNotSupported")); + }); + }); + + return promise; +} + +// Start test. +startTest(function() { + // Activate call barring service. + return testCallBarring(true) + // Deactivate call barring service. + .then(() => testCallBarring(false)) + .then(() => testUnsupportType()) + .then(() => testUnsupportedOperation()) + + .catch(error => ok(false, "Promise reject: " + error)) + .then(finish);; +}); diff --git a/dom/telephony/test/marionette/test_mmi_call_waiting.js b/dom/telephony/test/marionette/test_mmi_call_waiting.js new file mode 100644 index 000000000000..e26eb291aa39 --- /dev/null +++ b/dom/telephony/test/marionette/test_mmi_call_waiting.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +const TEST_DATA = [ + // [mmi, expectedError] + // Currently emulator doesn't support REQUEST_QUERY_CALL_WAITING, so we expect + // to get a 'RequestNotSupported' error here. + ["*#43*10#", "RequestNotSupported"], + // Currently emulator doesn't support REQUEST_SET_CALL_WAITING, so we expect + // to get a 'RequestNotSupported' error here. + ["*43*10#", "RequestNotSupported"], + ["#43*10#", "RequestNotSupported"], + // Unsupported Call Waiting MMI code. + ["**43*10#", "emMmiErrorNotSupported"], + ["##43*10#", "emMmiErrorNotSupported"], +]; + +function testCallWaiting(aMmi, aExpectedError) { + log("Test " + aMmi + " ..."); + + return gSendMMI(aMmi).then(aResult => { + // Since emulator doesn't support call waiting, so we expect the result is + // always failed. + ok(!aResult.success, "Check success"); + is(aResult.serviceCode, "scCallWaiting", "Check serviceCode"); + is(aResult.statusMessage, aExpectedError, "Check statusMessage"); + }); +} + +// Start test +startTest(function() { + let promise = Promise.resolve(); + + TEST_DATA.forEach(function(aData) { + promise = promise.then(() => testCallWaiting(aData[0], aData[1])); + }); + + return promise + .catch(aError => ok(false, "Promise reject: " + aError)) + .then(finish); +}); diff --git a/dom/telephony/test/marionette/test_mmi_change_pin2.js b/dom/telephony/test/marionette/test_mmi_change_pin2.js new file mode 100644 index 000000000000..c073b98310eb --- /dev/null +++ b/dom/telephony/test/marionette/test_mmi_change_pin2.js @@ -0,0 +1,105 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +const TEST_DATA = [ + // Test passing no pin2. + { + pin2: "", + newPin2: "0000", + newPin2Again: "1111", + expectedError: { + name: "emMmiError", + additionalInformation: null + } + }, + // Test passing no newPin2. + { + pin2: "0000", + newPin2: "", + newPin2Again: "", + expectedError: { + name: "emMmiError", + additionalInformation: null + } + }, + // Test passing mismatched newPin2. + { + pin2: "0000", + newPin2: "0000", + newPin2Again: "1111", + expectedError: { + name: "emMmiErrorMismatchPin", + additionalInformation: null + } + }, + // Test passing invalid pin2 (< 4 digit). + { + pin2: "123", + newPin2: "0000", + newPin2Again: "0000", + expectedError: { + name: "emMmiErrorInvalidPin", + additionalInformation: null + } + }, + // Test passing invalid newPin2 (> 8 digit). + { + pin2: "0000", + newPin2: "123456789", + newPin2Again: "123456789", + expectedError: { + name: "emMmiErrorInvalidPin", + additionalInformation: null + } + }, + // Test passing valid pin2 and newPin2. But currently emulator doesn't support + // REQUEST_CHANGE_SIM_PIN2, so we expect to get a 'RequestNotSupported' error + // here. + { + pin2: "0000", + newPin2: "0000", + newPin2Again: "0000", + expectedError: { + name: "RequestNotSupported", + additionalInformation: null + } + }, +]; + +function testChangePin2(aPin2, aNewPin2, aNewPin2Again, aExpectedError) { + let MMI_CODE = "**042*" + aPin2 + "*" + aNewPin2 + "*" + aNewPin2Again + "#"; + log("Test " + MMI_CODE); + + return gSendMMI(MMI_CODE).then(aResult => { + is(aResult.success, !aExpectedError, "check success"); + is(aResult.serviceCode, "scPin2", "Check service code"); + + if (aResult.success) { + is(aResult.statusMessage, "smPin2Changed", "Check status message"); + is(aResult.additionalInformation, undefined, "Check additional information"); + } else { + is(aResult.statusMessage, aExpectedError.name, "Check name"); + is(aResult.additionalInformation, aExpectedError.additionalInformation, + "Check additional information"); + } + }); +} + +// Start test +startTest(function() { + let promise = Promise.resolve(); + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => testChangePin2(data.pin2, + data.newPin2, + data.newPin2Again, + data.expectedError)); + } + + return promise + .catch(error => ok(false, "Promise reject: " + error)) + .then(finish); +}); diff --git a/dom/telephony/test/marionette/test_mmi_clip.js b/dom/telephony/test/marionette/test_mmi_clip.js new file mode 100644 index 000000000000..c878c3eb765d --- /dev/null +++ b/dom/telephony/test/marionette/test_mmi_clip.js @@ -0,0 +1,42 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +const TEST_DATA = [ + // [mmi, expectedError] + // Currently emulator doesn't support REQUEST_QUERY_CLIP, so we expect to + // get a 'RequestNotSupported' error here. + ["*#30#", "RequestNotSupported"], + // Unsupported CLIP MMI code. + ["*30#", "emMmiErrorNotSupported"], + ["#30#", "emMmiErrorNotSupported"], + ["**30#", "emMmiErrorNotSupported"], + ["##30#", "emMmiErrorNotSupported"], +]; + +function testCLIP(aMmi, aExpectedError) { + log("Test " + aMmi + " ..."); + + return gSendMMI(aMmi).then(aResult => { + // Since emulator doesn't support clip, so we expect the result is always + // failed. + ok(!aResult.success, "Check success"); + is(aResult.serviceCode, "scClip", "Check serviceCode"); + is(aResult.statusMessage, aExpectedError, "Check statusMessage"); + }); +} + +// Start test +startTest(function() { + let promise = Promise.resolve(); + + TEST_DATA.forEach(function(aData) { + promise = promise.then(() => testCLIP(aData[0], aData[1])); + }); + + return promise + .catch(aError => ok(false, "Promise reject: " + aError)) + .then(finish); +}); diff --git a/dom/telephony/test/marionette/test_mmi_clir.js b/dom/telephony/test/marionette/test_mmi_clir.js new file mode 100644 index 000000000000..ff384276de33 --- /dev/null +++ b/dom/telephony/test/marionette/test_mmi_clir.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +const TEST_DATA = [ + // [mmi, expectedError] + // Currently emulator doesn't support REQUEST_GET_CLIR, so we expect to get a + // 'RequestNotSupported' error here. + ["*#31#", "RequestNotSupported"], + // Currently emulator doesn't support REQUEST_SET_CLIR, so we expect to get a + // 'RequestNotSupported' error here. + ["*31#", "RequestNotSupported"], + ["#31#", "RequestNotSupported"], + // Unsupported CLIR MMI code. + ["**31#", "emMmiErrorNotSupported"], + ["##31#", "emMmiErrorNotSupported"], +]; + +function testCLIR(aMmi, aExpectedError) { + log("Test " + aMmi + " ..."); + + return gSendMMI(aMmi).then(aResult => { + // Since emulator doesn't support clip, so we expect the result is always + // failed. + ok(!aResult.success, "Check success"); + is(aResult.serviceCode, "scClir", "Check serviceCode"); + is(aResult.statusMessage, aExpectedError, "Check statusMessage"); + }); +} + +// Start test +startTest(function() { + let promise = Promise.resolve(); + + TEST_DATA.forEach(function(aData) { + promise = promise.then(() => testCLIR(aData[0], aData[1])); + }); + + return promise + .catch(aError => ok(false, "Promise reject: " + aError)) + .then(finish); +}); diff --git a/dom/telephony/test/marionette/test_mmi.js b/dom/telephony/test/marionette/test_mmi_imei.js similarity index 100% rename from dom/telephony/test/marionette/test_mmi.js rename to dom/telephony/test/marionette/test_mmi_imei.js diff --git a/dom/telephony/test/marionette/test_mmi_unlock_puk2.js b/dom/telephony/test/marionette/test_mmi_unlock_puk2.js new file mode 100644 index 000000000000..fe52939e7f61 --- /dev/null +++ b/dom/telephony/test/marionette/test_mmi_unlock_puk2.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +function sendUnlockPuk2Mmi(aPuk2, aNewPin2, aNewPin2Again) { + let MMI_CODE = "**052*" + aPuk2 + "*" + aNewPin2 + "*" + aNewPin2Again + "#"; + log("Test " + MMI_CODE); + + return gSendMMI(MMI_CODE); +} + +function testUnlockPuk2MmiError(aPuk2, aNewPin2, aNewPin2Again, aErrorName) { + return sendUnlockPuk2Mmi(aPuk2, aNewPin2, aNewPin2Again) + .then((aResult) => { + ok(!aResult.success, "Check success"); + is(aResult.serviceCode, "scPuk2", "Check service code"); + is(aResult.statusMessage, aErrorName, "Check statusMessage"); + is(aResult.additionalInformation, null, "Check additional information"); + }); +} + +// Start test +startTest(function() { + return Promise.resolve() + // Test passing no puk2. + .then(() => testUnlockPuk2MmiError("", "1111", "2222", "emMmiError")) + // Test passing no newPin2. + .then(() => testUnlockPuk2MmiError("11111111", "", "", "emMmiError")) + // Test passing mismatched newPin2. + .then(() => testUnlockPuk2MmiError("11111111", "1111", "2222", + "emMmiErrorMismatchPin")) + // Test passing invalid puk2 (> 8 digit). + .then(() => testUnlockPuk2MmiError("123456789", "0000", "0000", + "emMmiErrorInvalidPin")) + // Test passing valid puk2 and newPin2. But currently emulator doesn't + // support RIL_REQUEST_ENTER_SIM_PUK2, so we expect to get a + // 'RequestNotSupported' error here. + .then(() => testUnlockPuk2MmiError("11111111", "0000", "0000", + "RequestNotSupported")) + .catch(error => ok(false, "Promise reject: " + error)) + .then(finish); +}); diff --git a/dom/telephony/test/marionette/test_mmi_ussd.js b/dom/telephony/test/marionette/test_mmi_ussd.js new file mode 100644 index 000000000000..52a5e2575a3c --- /dev/null +++ b/dom/telephony/test/marionette/test_mmi_ussd.js @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +function testUSSD() { + log("Test *#1234# ..."); + + return gSendMMI("*#1234#").then(aResult => { + // Since emulator doesn't support sending USSD, so we expect the result is + // always failed. + ok(!aResult.success, "Check success"); + is(aResult.serviceCode, "scUssd", "Check serviceCode"); + is(aResult.statusMessage, "RequestNotSupported", "Check statusMessage"); + is(aResult.additionalInformation, undefined, "No additional information"); + }); +} + +// Start test +startTest(function() { + return testUSSD() + .catch(error => ok(false, "Promise reject: " + error)) + .then(finish); +}); diff --git a/dom/telephony/test/xpcshell/test_parseMMI.js b/dom/telephony/test/xpcshell/test_parseMMI.js index 566bf80053a7..74ffed2546f4 100644 --- a/dom/telephony/test/xpcshell/test_parseMMI.js +++ b/dom/telephony/test/xpcshell/test_parseMMI.js @@ -1,7 +1,8 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this); +let TS = {}; +subscriptLoader.loadSubScript("resource://gre/components/TelephonyService.js", TS); let NS = {}; subscriptLoader.loadSubScript("resource://gre/modules/DialNumberUtils.jsm", NS); @@ -50,7 +51,7 @@ add_test(function test_parseMMI_USSD() { let mmi = parseMMI("*123#"); equal(mmi.fullMMI, "*123#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, undefined); equal(mmi.sib, undefined); @@ -73,7 +74,7 @@ add_test(function test_parseMMI_sia() { let mmi = parseMMI("*123*1#"); equal(mmi.fullMMI, "*123*1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, "1"); equal(mmi.sib, undefined); @@ -88,7 +89,7 @@ add_test(function test_parseMMI_sib() { let mmi = parseMMI("*123**1#"); equal(mmi.fullMMI, "*123**1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, ""); equal(mmi.sib, "1"); @@ -103,7 +104,7 @@ add_test(function test_parseMMI_sic() { let mmi = parseMMI("*123***1#"); equal(mmi.fullMMI, "*123***1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, ""); equal(mmi.sib, ""); @@ -118,7 +119,7 @@ add_test(function test_parseMMI_sia_sib() { let mmi = parseMMI("*123*1*1#"); equal(mmi.fullMMI, "*123*1*1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, "1"); equal(mmi.sib, "1"); @@ -133,7 +134,7 @@ add_test(function test_parseMMI_sia_sic() { let mmi = parseMMI("*123*1**1#"); equal(mmi.fullMMI, "*123*1**1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, "1"); equal(mmi.sib, ""); @@ -148,7 +149,7 @@ add_test(function test_parseMMI_sib_sic() { let mmi = parseMMI("*123**1*1#"); equal(mmi.fullMMI, "*123**1*1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, ""); equal(mmi.sib, "1"); @@ -163,7 +164,7 @@ add_test(function test_parseMMI_pwd() { let mmi = parseMMI("*123****1#"); equal(mmi.fullMMI, "*123****1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, ""); equal(mmi.sib, ""); @@ -178,7 +179,7 @@ add_test(function test_parseMMI_dial_number() { let mmi = parseMMI("*123#345"); equal(mmi.fullMMI, "*123#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, undefined); equal(mmi.sib, undefined); @@ -198,7 +199,7 @@ add_test(function test_parseMMI_activation() { let mmi = parseMMI("*00*12*34*56#"); equal(mmi.fullMMI, "*00*12*34*56#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "00"); equal(mmi.sia, "12"); equal(mmi.sib, "34"); @@ -213,7 +214,7 @@ add_test(function test_parseMMI_deactivation() { let mmi = parseMMI("#00*12*34*56#"); equal(mmi.fullMMI, "#00*12*34*56#"); - equal(mmi.procedure, MMI_PROCEDURE_DEACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_DEACTIVATION); equal(mmi.serviceCode, "00"); equal(mmi.sia, "12"); equal(mmi.sib, "34"); @@ -228,7 +229,7 @@ add_test(function test_parseMMI_interrogation() { let mmi = parseMMI("*#00*12*34*56#"); equal(mmi.fullMMI, "*#00*12*34*56#"); - equal(mmi.procedure, MMI_PROCEDURE_INTERROGATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_INTERROGATION); equal(mmi.serviceCode, "00"); equal(mmi.sia, "12"); equal(mmi.sib, "34"); @@ -243,7 +244,7 @@ add_test(function test_parseMMI_registration() { let mmi = parseMMI("**00*12*34*56#"); equal(mmi.fullMMI, "**00*12*34*56#"); - equal(mmi.procedure, MMI_PROCEDURE_REGISTRATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_REGISTRATION); equal(mmi.serviceCode, "00"); equal(mmi.sia, "12"); equal(mmi.sib, "34"); @@ -258,7 +259,7 @@ add_test(function test_parseMMI_erasure() { let mmi = parseMMI("##00*12*34*56#"); equal(mmi.fullMMI, "##00*12*34*56#"); - equal(mmi.procedure, MMI_PROCEDURE_ERASURE); + equal(mmi.procedure, TS.MMI_PROCEDURE_ERASURE); equal(mmi.serviceCode, "00"); equal(mmi.sia, "12"); equal(mmi.sib, "34"); diff --git a/ipc/ril/Ril.cpp b/ipc/ril/Ril.cpp index 66c7a8d4c39c..01fbaa5e0f10 100644 --- a/ipc/ril/Ril.cpp +++ b/ipc/ril/Ril.cpp @@ -5,13 +5,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/ipc/Ril.h" - #include #include #include -#include // For gethostbyname. +#include "jsfriendapi.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/workers/Workers.h" +#include "mozilla/ipc/RilSocket.h" +#include "mozilla/ipc/RilSocketConsumer.h" +#include "nsThreadUtils.h" // For NS_IsMainThread. +#include "RilConnector.h" +#ifdef CHROMIUM_LOG #undef CHROMIUM_LOG +#endif + #if defined(MOZ_WIDGET_GONK) #include #define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) @@ -19,189 +28,95 @@ #define CHROMIUM_LOG(args...) printf(args); #endif -#include "jsfriendapi.h" -#include "mozilla/ArrayUtils.h" -#include "nsTArray.h" -#include "nsThreadUtils.h" // For NS_IsMainThread. -#include "RilConnector.h" +namespace mozilla { +namespace ipc { -USING_WORKERS_NAMESPACE -using namespace mozilla::ipc; +USING_WORKERS_NAMESPACE; +using namespace JS; -namespace { +class RilConsumer; static const char RIL_SOCKET_NAME[] = "/dev/socket/rilproxy"; -static nsTArray> sRilConsumers; +static nsTArray> sRilConsumers; -class ConnectWorkerToRIL final : public WorkerTask +// +// RilConsumer +// + +class RilConsumer final : public RilSocketConsumer { public: - bool RunTask(JSContext* aCx) override; -}; + RilConsumer(); -class SendRilSocketDataTask final : public nsRunnable -{ -public: - SendRilSocketDataTask(unsigned long aClientId, - UnixSocketRawData* aRawData) - : mRawData(aRawData) - , mClientId(aClientId) - { } + nsresult ConnectWorkerToRIL(JSContext* aCx); - NS_IMETHOD Run() override - { - MOZ_ASSERT(NS_IsMainThread()); + nsresult Register(unsigned long aClientId, + WorkerCrossThreadDispatcher* aDispatcher); + void Unregister(); - if (sRilConsumers.Length() <= mClientId || !sRilConsumers[mClientId]) { - // Probably shutting down. - delete mRawData; - return NS_OK; - } + // Methods for |RilSocketConsumer| + // - sRilConsumers[mClientId]->Send(mRawData); - return NS_OK; - } + void ReceiveSocketData(JSContext* aCx, + int aIndex, + nsAutoPtr& aBuffer) override; + void OnConnectSuccess(int aIndex) override; + void OnConnectError(int aIndex) override; + void OnDisconnect(int aIndex) override; + +protected: + static bool PostRILMessage(JSContext* aCx, unsigned aArgc, Value* aVp); + + nsresult Send(JSContext* aCx, const CallArgs& aArgs); + nsresult Receive(JSContext* aCx, + uint32_t aClientId, + const UnixSocketBuffer* aBuffer); + void Close(); private: - UnixSocketRawData* mRawData; - unsigned long mClientId; + nsRefPtr mSocket; + nsCString mAddress; + bool mShutdown; }; -static bool -PostToRIL(JSContext* aCx, unsigned aArgc, JS::Value* aVp) -{ - JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); - NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread"); +RilConsumer::RilConsumer() + : mShutdown(false) +{ } - if (args.length() != 2) { - JS_ReportError(aCx, "Expecting two arguments with the RIL message"); - return false; - } - - int clientId = args[0].toInt32(); - JS::Value v = args[1]; - - UnixSocketRawData* raw = nullptr; - - if (v.isString()) { - JSAutoByteString abs; - JS::Rooted str(aCx, v.toString()); - if (!abs.encodeUtf8(aCx, str)) { - return false; - } - - raw = new UnixSocketRawData(abs.ptr(), abs.length()); - } else if (!v.isPrimitive()) { - JSObject* obj = v.toObjectOrNull(); - if (!JS_IsTypedArrayObject(obj)) { - JS_ReportError(aCx, "Object passed in wasn't a typed array"); - return false; - } - - uint32_t type = JS_GetArrayBufferViewType(obj); - if (type != js::Scalar::Int8 && - type != js::Scalar::Uint8 && - type != js::Scalar::Uint8Clamped) { - JS_ReportError(aCx, "Typed array data is not octets"); - return false; - } - - JS::AutoCheckCannotGC nogc; - size_t size = JS_GetTypedArrayByteLength(obj); - void* data = JS_GetArrayBufferViewData(obj, nogc); - raw = new UnixSocketRawData(data, size); - } else { - JS_ReportError( - aCx, "Incorrect argument. Expecting a string or a typed array"); - return false; - } - - if (!raw) { - JS_ReportError(aCx, "Unable to post to RIL"); - return false; - } - - nsRefPtr task = new SendRilSocketDataTask(clientId, - raw); - NS_DispatchToMainThread(task); - return true; -} - -bool -ConnectWorkerToRIL::RunTask(JSContext* aCx) +nsresult +RilConsumer::ConnectWorkerToRIL(JSContext* aCx) { // Set up the postRILMessage on the function for worker -> RIL thread // communication. - NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread"); NS_ASSERTION(!JS_IsRunning(aCx), "Are we being called somehow?"); - JS::Rooted workerGlobal(aCx, JS::CurrentGlobalOrNull(aCx)); + Rooted workerGlobal(aCx, CurrentGlobalOrNull(aCx)); // Check whether |postRILMessage| has been defined. No one but this class // should ever define |postRILMessage| in a RIL worker. - JS::Rooted val(aCx); + Rooted val(aCx); if (!JS_GetProperty(aCx, workerGlobal, "postRILMessage", &val)) { JS_ReportPendingException(aCx); - return false; + return NS_ERROR_FAILURE; } // Make sure that |postRILMessage| is a function. if (JSTYPE_FUNCTION == JS_TypeOfValue(aCx, val)) { - return true; + return NS_OK; } - return !!JS_DefineFunction(aCx, workerGlobal, "postRILMessage", - PostToRIL, 2, 0); + JSFunction* postRILMessage = JS_DefineFunction(aCx, workerGlobal, + "postRILMessage", + PostRILMessage, 2, 0); + if (NS_WARN_IF(!postRILMessage)) { + return NS_ERROR_FAILURE; + } + return NS_OK; } -class DispatchRILEvent final : public WorkerTask -{ -public: - DispatchRILEvent(unsigned long aClient, UnixSocketBuffer* aBuffer) - : mClientId(aClient) - , mBuffer(aBuffer) - { } - - bool RunTask(JSContext* aCx) override; - -private: - unsigned long mClientId; - nsAutoPtr mBuffer; -}; - -bool -DispatchRILEvent::RunTask(JSContext* aCx) -{ - JS::Rooted obj(aCx, JS::CurrentGlobalOrNull(aCx)); - - JS::Rooted array(aCx, - JS_NewUint8Array(aCx, mBuffer->GetSize())); - if (!array) { - return false; - } - { - JS::AutoCheckCannotGC nogc; - memcpy(JS_GetArrayBufferViewData(array, nogc), - mBuffer->GetData(), mBuffer->GetSize()); - } - - JS::AutoValueArray<2> args(aCx); - args[0].setNumber((uint32_t)mClientId); - args[1].setObject(*array); - - JS::Rooted rval(aCx); - return JS_CallFunctionName(aCx, obj, "onRILMessage", args, &rval); -} - -} // namespace - -namespace mozilla { -namespace ipc { - -RilConsumer::RilConsumer(unsigned long aClientId, - WorkerCrossThreadDispatcher* aDispatcher) - : mDispatcher(aDispatcher) - , mShutdown(false) +nsresult +RilConsumer::Register(unsigned long aClientId, + WorkerCrossThreadDispatcher* aDispatcher) { // Only append client id after RIL_SOCKET_NAME when it's not connected to // the first(0) rilproxy for compatibility. @@ -214,60 +129,131 @@ RilConsumer::RilConsumer(unsigned long aClientId, mAddress = addr_un.sun_path; } - mSocket = new StreamSocket(this, aClientId); - mSocket->Connect(new RilConnector(mAddress, aClientId)); -} + mSocket = new RilSocket(aDispatcher, this, aClientId); -nsresult -RilConsumer::Register(unsigned int aClientId, - WorkerCrossThreadDispatcher* aDispatcher) -{ - MOZ_ASSERT(NS_IsMainThread()); - - sRilConsumers.EnsureLengthAtLeast(aClientId + 1); - - if (sRilConsumers[aClientId]) { - NS_WARNING("RilConsumer already registered"); - return NS_ERROR_FAILURE; + nsresult rv = mSocket->Connect(new RilConnector(mAddress, aClientId)); + if (NS_FAILED(rv)) { + return rv; } - nsRefPtr connection = new ConnectWorkerToRIL(); - if (!aDispatcher->PostTask(connection)) { - NS_WARNING("Failed to connect worker to ril"); - return NS_ERROR_UNEXPECTED; - } - - // Now that we're set up, connect ourselves to the RIL thread. - sRilConsumers[aClientId] = new RilConsumer(aClientId, aDispatcher); return NS_OK; } void -RilConsumer::Shutdown() +RilConsumer::Unregister() { - MOZ_ASSERT(NS_IsMainThread()); - - for (unsigned long i = 0; i < sRilConsumers.Length(); i++) { - nsAutoPtr instance(sRilConsumers[i]); - if (!instance) { - continue; - } - - instance->mShutdown = true; - instance->Close(); - instance = nullptr; - } + mShutdown = true; + Close(); } -void -RilConsumer::Send(UnixSocketRawData* aRawData) +bool +RilConsumer::PostRILMessage(JSContext* aCx, unsigned aArgc, Value* aVp) { - if (!mSocket || mSocket->GetConnectionStatus() != SOCKET_CONNECTED) { - // Probably shutting down. - delete aRawData; - return; + CallArgs args = CallArgsFromVp(aArgc, aVp); + + if (args.length() != 2) { + JS_ReportError(aCx, "Expecting two arguments with the RIL message"); + return false; } - mSocket->SendSocketData(aRawData); + + int clientId = args[0].toInt32(); + + if ((ssize_t)sRilConsumers.Length() <= clientId || !sRilConsumers[clientId]) { + // Probably shutting down. + return true; + } + + nsresult rv = sRilConsumers[clientId]->Send(aCx, args); + if (NS_FAILED(rv)) { + return false; + } + + return true; +} + +nsresult +RilConsumer::Send(JSContext* aCx, const CallArgs& aArgs) +{ + if (NS_WARN_IF(!mSocket) || + NS_WARN_IF(mSocket->GetConnectionStatus() == SOCKET_DISCONNECTED)) { + // Probably shutting down. + return NS_OK; + } + + nsAutoPtr raw; + + Value v = aArgs[1]; + + if (v.isString()) { + JSAutoByteString abs; + Rooted str(aCx, v.toString()); + if (!abs.encodeUtf8(aCx, str)) { + return NS_ERROR_FAILURE; + } + + raw = new UnixSocketRawData(abs.ptr(), abs.length()); + } else if (!v.isPrimitive()) { + JSObject* obj = v.toObjectOrNull(); + if (!JS_IsTypedArrayObject(obj)) { + JS_ReportError(aCx, "Object passed in wasn't a typed array"); + return NS_ERROR_FAILURE; + } + + uint32_t type = JS_GetArrayBufferViewType(obj); + if (type != js::Scalar::Int8 && + type != js::Scalar::Uint8 && + type != js::Scalar::Uint8Clamped) { + JS_ReportError(aCx, "Typed array data is not octets"); + return NS_ERROR_FAILURE; + } + + AutoCheckCannotGC nogc; + size_t size = JS_GetTypedArrayByteLength(obj); + void* data = JS_GetArrayBufferViewData(obj, nogc); + raw = new UnixSocketRawData(data, size); + } else { + JS_ReportError( + aCx, "Incorrect argument. Expecting a string or a typed array"); + return NS_ERROR_FAILURE; + } + + if (!raw) { + JS_ReportError(aCx, "Unable to post to RIL"); + return NS_ERROR_FAILURE; + } + + mSocket->SendSocketData(raw.forget()); + + return NS_OK; +} + +nsresult +RilConsumer::Receive(JSContext* aCx, + uint32_t aClientId, + const UnixSocketBuffer* aBuffer) +{ + MOZ_ASSERT(aBuffer); + + Rooted obj(aCx, CurrentGlobalOrNull(aCx)); + + Rooted array(aCx, JS_NewUint8Array(aCx, aBuffer->GetSize())); + if (NS_WARN_IF(!array)) { + return NS_ERROR_FAILURE; + } + { + AutoCheckCannotGC nogc; + memcpy(JS_GetArrayBufferViewData(array, nogc), + aBuffer->GetData(), aBuffer->GetSize()); + } + + AutoValueArray<2> args(aCx); + args[0].setNumber(aClientId); + args[1].setObject(*array); + + Rooted rval(aCx); + JS_CallFunctionName(aCx, obj, "onRILMessage", args, &rval); + + return NS_OK; } void @@ -279,16 +265,14 @@ RilConsumer::Close() } } -// |StreamSocketConnector| +// |RilSocketConnector| void -RilConsumer::ReceiveSocketData(int aIndex, +RilConsumer::ReceiveSocketData(JSContext* aCx, + int aIndex, nsAutoPtr& aBuffer) { - MOZ_ASSERT(NS_IsMainThread()); - - nsRefPtr dre(new DispatchRILEvent(aIndex, aBuffer.forget())); - mDispatcher->PostTask(dre); + Receive(aCx, (uint32_t)aIndex, aBuffer); } void @@ -316,5 +300,140 @@ RilConsumer::OnDisconnect(int aIndex) mSocket->GetSuggestedConnectDelayMs()); } +// +// RilWorker +// + +nsTArray> RilWorker::sRilWorkers; + +nsresult +RilWorker::Register(unsigned int aClientId, + WorkerCrossThreadDispatcher* aDispatcher) +{ + MOZ_ASSERT(NS_IsMainThread()); + + sRilWorkers.EnsureLengthAtLeast(aClientId + 1); + + if (sRilWorkers[aClientId]) { + NS_WARNING("RilWorkers already registered"); + return NS_ERROR_FAILURE; + } + + // Now that we're set up, connect ourselves to the RIL thread. + sRilWorkers[aClientId] = new RilWorker(aDispatcher); + + nsresult rv = sRilWorkers[aClientId]->RegisterConsumer(aClientId); + if (NS_FAILED(rv)) { + return rv; + } + + return NS_OK; +} + +void +RilWorker::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + + for (size_t i = 0; i < sRilWorkers.Length(); ++i) { + if (!sRilWorkers[i]) { + continue; + } + sRilWorkers[i]->UnregisterConsumer(i); + sRilWorkers[i] = nullptr; + } +} + +RilWorker::RilWorker(WorkerCrossThreadDispatcher* aDispatcher) + : mDispatcher(aDispatcher) +{ + MOZ_ASSERT(mDispatcher); +} + +class RilWorker::RegisterConsumerTask : public WorkerTask +{ +public: + RegisterConsumerTask(unsigned int aClientId, + WorkerCrossThreadDispatcher* aDispatcher) + : mClientId(aClientId) + , mDispatcher(aDispatcher) + { + MOZ_ASSERT(mDispatcher); + } + + bool RunTask(JSContext* aCx) override + { + sRilConsumers.EnsureLengthAtLeast(mClientId + 1); + + MOZ_ASSERT(!sRilConsumers[mClientId]); + + nsAutoPtr rilConsumer(new RilConsumer()); + + nsresult rv = rilConsumer->ConnectWorkerToRIL(aCx); + if (NS_FAILED(rv)) { + return false; + } + + rv = rilConsumer->Register(mClientId, mDispatcher); + if (NS_FAILED(rv)) { + return false; + } + sRilConsumers[mClientId] = rilConsumer; + + return true; + } + +private: + unsigned int mClientId; + nsRefPtr mDispatcher; +}; + +nsresult +RilWorker::RegisterConsumer(unsigned int aClientId) +{ + nsRefPtr task = new RegisterConsumerTask(aClientId, + mDispatcher); + if (!mDispatcher->PostTask(task)) { + NS_WARNING("Failed to post register-consumer task."); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +class RilWorker::UnregisterConsumerTask : public WorkerTask +{ +public: + UnregisterConsumerTask(unsigned int aClientId) + : mClientId(aClientId) + { } + + bool RunTask(JSContext* aCx) override + { + MOZ_ASSERT(mClientId < sRilConsumers.Length()); + MOZ_ASSERT(sRilConsumers[mClientId]); + + sRilConsumers[mClientId]->Unregister(); + sRilConsumers[mClientId] = nullptr; + + return true; + } + +private: + unsigned int mClientId; +}; + +void +RilWorker::UnregisterConsumer(unsigned int aClientId) +{ + nsRefPtr task = + new UnregisterConsumerTask(aClientId); + + if (!mDispatcher->PostTask(task)) { + NS_WARNING("Failed to post unregister-consumer task."); + return; + } +} + } // namespace ipc } // namespace mozilla diff --git a/ipc/ril/Ril.h b/ipc/ril/Ril.h index 5d0ae62eff02..e51728db0727 100644 --- a/ipc/ril/Ril.h +++ b/ipc/ril/Ril.h @@ -7,42 +7,45 @@ #ifndef mozilla_ipc_Ril_h #define mozilla_ipc_Ril_h 1 -#include -#include -#include +#include "nsAutoPtr.h" +#include "nsError.h" +#include "nsTArray.h" namespace mozilla { + +namespace dom { +namespace workers { + +class WorkerCrossThreadDispatcher; + +} // namespace workers +} // namespace dom + namespace ipc { -class RilConsumer final : public StreamSocketConsumer +class RilConsumer; + +class RilWorker final { public: static nsresult Register( unsigned int aClientId, mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher); + static void Shutdown(); - void Send(UnixSocketRawData* aRawData); - private: - RilConsumer(unsigned long aClientId, - mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher); + class RegisterConsumerTask; + class UnregisterConsumerTask; - void Close(); + RilWorker(mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher); - // Methods for |StreamSocketConsumer| - // + nsresult RegisterConsumer(unsigned int aClientId); + void UnregisterConsumer(unsigned int aClientId); - void ReceiveSocketData(int aIndex, - nsAutoPtr& aBuffer) override; - void OnConnectSuccess(int aIndex) override; - void OnConnectError(int aIndex) override; - void OnDisconnect(int aIndex) override; + static nsTArray> sRilWorkers; - nsRefPtr mSocket; nsRefPtr mDispatcher; - nsCString mAddress; - bool mShutdown; }; } // namespace ipc diff --git a/ipc/ril/RilSocket.cpp b/ipc/ril/RilSocket.cpp new file mode 100644 index 000000000000..48db874dc1a1 --- /dev/null +++ b/ipc/ril/RilSocket.cpp @@ -0,0 +1,433 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "RilSocket.h" +#include +#include "mozilla/dom/workers/Workers.h" +#include "mozilla/ipc/UnixSocketConnector.h" +#include "mozilla/RefPtr.h" +#include "nsXULAppAPI.h" +#include "RilSocketConsumer.h" + +static const size_t MAX_READ_SIZE = 1 << 16; + +namespace mozilla { +namespace ipc { + +USING_WORKERS_NAMESPACE + +// +// RilSocketIO +// + +class RilSocketIO final : public ConnectionOrientedSocketIO +{ +public: + class ConnectTask; + class DelayedConnectTask; + class ReceiveTask; + + RilSocketIO(WorkerCrossThreadDispatcher* aDispatcher, + MessageLoop* aConsumerLoop, + MessageLoop* aIOLoop, + RilSocket* aRilSocket, + UnixSocketConnector* aConnector); + ~RilSocketIO(); + + RilSocket* GetRilSocket(); + DataSocket* GetDataSocket(); + + // Delayed-task handling + // + + void SetDelayedConnectTask(CancelableTask* aTask); + void ClearDelayedConnectTask(); + void CancelDelayedConnectTask(); + + // Methods for |DataSocket| + // + + nsresult QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer) override; + void ConsumeBuffer() override; + void DiscardBuffer() override; + + // Methods for |SocketIOBase| + // + + SocketBase* GetSocketBase() override; + + bool IsShutdownOnConsumerThread() const override; + bool IsShutdownOnIOThread() const override; + + void ShutdownOnConsumerThread() override; + void ShutdownOnIOThread() override; + +private: + /** + * Cross-thread dispatcher for the RIL worker + */ + nsRefPtr mDispatcher; + + /** + * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated + * directly from consumer thread. All non-consumer-thread accesses should + * happen with mIO as container. + */ + RefPtr mRilSocket; + + /** + * If true, do not requeue whatever task we're running + */ + bool mShuttingDownOnIOThread; + + /** + * Task member for delayed connect task. Should only be access on consumer + * thread. + */ + CancelableTask* mDelayedConnectTask; + + /** + * I/O buffer for received data + */ + nsAutoPtr mBuffer; +}; + +RilSocketIO::RilSocketIO(WorkerCrossThreadDispatcher* aDispatcher, + MessageLoop* aConsumerLoop, + MessageLoop* aIOLoop, + RilSocket* aRilSocket, + UnixSocketConnector* aConnector) + : ConnectionOrientedSocketIO(aConsumerLoop, aIOLoop, aConnector) + , mDispatcher(aDispatcher) + , mRilSocket(aRilSocket) + , mShuttingDownOnIOThread(false) + , mDelayedConnectTask(nullptr) +{ + MOZ_ASSERT(mDispatcher); + MOZ_ASSERT(mRilSocket); +} + +RilSocketIO::~RilSocketIO() +{ + MOZ_ASSERT(IsConsumerThread()); + MOZ_ASSERT(IsShutdownOnConsumerThread()); +} + +RilSocket* +RilSocketIO::GetRilSocket() +{ + return mRilSocket.get(); +} + +DataSocket* +RilSocketIO::GetDataSocket() +{ + return mRilSocket.get(); +} + +void +RilSocketIO::SetDelayedConnectTask(CancelableTask* aTask) +{ + MOZ_ASSERT(IsConsumerThread()); + + mDelayedConnectTask = aTask; +} + +void +RilSocketIO::ClearDelayedConnectTask() +{ + MOZ_ASSERT(IsConsumerThread()); + + mDelayedConnectTask = nullptr; +} + +void +RilSocketIO::CancelDelayedConnectTask() +{ + MOZ_ASSERT(IsConsumerThread()); + + if (!mDelayedConnectTask) { + return; + } + + mDelayedConnectTask->Cancel(); + ClearDelayedConnectTask(); +} + +// |DataSocketIO| + +nsresult +RilSocketIO::QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer) +{ + MOZ_ASSERT(aBuffer); + + if (!mBuffer) { + mBuffer = new UnixSocketRawData(MAX_READ_SIZE); + } + *aBuffer = mBuffer.get(); + + return NS_OK; +} + +/** + * |ReceiveTask| transfers data received on the I/O thread + * to an instance of |RilSocket| on the consumer thread. + */ +class RilSocketIO::ReceiveTask final : public WorkerTask +{ +public: + ReceiveTask(RilSocketIO* aIO, UnixSocketBuffer* aBuffer) + : mIO(aIO) + , mBuffer(aBuffer) + { + MOZ_ASSERT(mIO); + } + + bool RunTask(JSContext* aCx) override + { + // Dispatched via WCTD, but still needs to run on the consumer thread + MOZ_ASSERT(mIO->IsConsumerThread()); + + if (NS_WARN_IF(mIO->IsShutdownOnConsumerThread())) { + // Since we've already explicitly closed and the close + // happened before this, this isn't really an error. + return true; + } + + RilSocket* rilSocket = mIO->GetRilSocket(); + MOZ_ASSERT(rilSocket); + + rilSocket->ReceiveSocketData(aCx, mBuffer); + + return true; + } + +private: + RilSocketIO* mIO; + nsAutoPtr mBuffer; +}; + +void +RilSocketIO::ConsumeBuffer() +{ + nsRefPtr task = new ReceiveTask(this, mBuffer.forget()); + NS_WARN_IF(!mDispatcher->PostTask(task)); +} + +void +RilSocketIO::DiscardBuffer() +{ + // Nothing to do. +} + +// |SocketIOBase| + +SocketBase* +RilSocketIO::GetSocketBase() +{ + return GetDataSocket(); +} + +bool +RilSocketIO::IsShutdownOnConsumerThread() const +{ + MOZ_ASSERT(IsConsumerThread()); + + return mRilSocket == nullptr; +} + +bool +RilSocketIO::IsShutdownOnIOThread() const +{ + return mShuttingDownOnIOThread; +} + +void +RilSocketIO::ShutdownOnConsumerThread() +{ + MOZ_ASSERT(IsConsumerThread()); + MOZ_ASSERT(!IsShutdownOnConsumerThread()); + + mRilSocket = nullptr; +} + +void +RilSocketIO::ShutdownOnIOThread() +{ + MOZ_ASSERT(!IsConsumerThread()); + MOZ_ASSERT(!mShuttingDownOnIOThread); + + Close(); // will also remove fd from I/O loop + mShuttingDownOnIOThread = true; +} + +// +// Socket tasks +// + +class RilSocketIO::ConnectTask final + : public SocketIOTask +{ +public: + ConnectTask(RilSocketIO* aIO) + : SocketIOTask(aIO) + { } + + void Run() override + { + MOZ_ASSERT(!GetIO()->IsConsumerThread()); + MOZ_ASSERT(!IsCanceled()); + + GetIO()->Connect(); + } +}; + +class RilSocketIO::DelayedConnectTask final + : public SocketIOTask +{ +public: + DelayedConnectTask(RilSocketIO* aIO) + : SocketIOTask(aIO) + { } + + void Run() override + { + MOZ_ASSERT(GetIO()->IsConsumerThread()); + + if (IsCanceled()) { + return; + } + + RilSocketIO* io = GetIO(); + if (io->IsShutdownOnConsumerThread()) { + return; + } + + io->ClearDelayedConnectTask(); + io->GetIOLoop()->PostTask(FROM_HERE, new ConnectTask(io)); + } +}; + +// +// RilSocket +// + +RilSocket::RilSocket(WorkerCrossThreadDispatcher* aDispatcher, + RilSocketConsumer* aConsumer, int aIndex) + : mIO(nullptr) + , mDispatcher(aDispatcher) + , mConsumer(aConsumer) + , mIndex(aIndex) +{ + MOZ_ASSERT(mDispatcher); + MOZ_ASSERT(mConsumer); +} + +RilSocket::~RilSocket() +{ + MOZ_ASSERT(!mIO); +} + +void +RilSocket::ReceiveSocketData(JSContext* aCx, + nsAutoPtr& aBuffer) +{ + mConsumer->ReceiveSocketData(aCx, mIndex, aBuffer); +} + +nsresult +RilSocket::Connect(UnixSocketConnector* aConnector, int aDelayMs, + MessageLoop* aConsumerLoop, MessageLoop* aIOLoop) +{ + MOZ_ASSERT(!mIO); + + mIO = new RilSocketIO(mDispatcher, aConsumerLoop, aIOLoop, this, aConnector); + SetConnectionStatus(SOCKET_CONNECTING); + + if (aDelayMs > 0) { + RilSocketIO::DelayedConnectTask* connectTask = + new RilSocketIO::DelayedConnectTask(mIO); + mIO->SetDelayedConnectTask(connectTask); + MessageLoop::current()->PostDelayedTask(FROM_HERE, connectTask, aDelayMs); + } else { + aIOLoop->PostTask(FROM_HERE, new RilSocketIO::ConnectTask(mIO)); + } + + return NS_OK; +} + +nsresult +RilSocket::Connect(UnixSocketConnector* aConnector, int aDelayMs) +{ + return Connect(aConnector, aDelayMs, + MessageLoop::current(), XRE_GetIOMessageLoop()); +} + +// |ConnectionOrientedSocket| + +nsresult +RilSocket::PrepareAccept(UnixSocketConnector* aConnector, + MessageLoop* aConsumerLoop, + MessageLoop* aIOLoop, + ConnectionOrientedSocketIO*& aIO) +{ + MOZ_CRASH("|RilSocket| does not support accepting connections."); +} + +// |DataSocket| + +void +RilSocket::SendSocketData(UnixSocketIOBuffer* aBuffer) +{ + MOZ_ASSERT(mIO); + MOZ_ASSERT(mIO->IsConsumerThread()); + MOZ_ASSERT(!mIO->IsShutdownOnConsumerThread()); + + mIO->GetIOLoop()->PostTask( + FROM_HERE, + new SocketIOSendTask(mIO, aBuffer)); +} + +// |SocketBase| + +void +RilSocket::Close() +{ + MOZ_ASSERT(mIO); + MOZ_ASSERT(mIO->IsConsumerThread()); + + mIO->CancelDelayedConnectTask(); + + // From this point on, we consider |mIO| as being deleted. We sever + // the relationship here so any future calls to |Connect| will create + // a new I/O object. + mIO->ShutdownOnConsumerThread(); + mIO->GetIOLoop()->PostTask(FROM_HERE, new SocketIOShutdownTask(mIO)); + mIO = nullptr; + + NotifyDisconnect(); +} + +void +RilSocket::OnConnectSuccess() +{ + mConsumer->OnConnectSuccess(mIndex); +} + +void +RilSocket::OnConnectError() +{ + mConsumer->OnConnectError(mIndex); +} + +void +RilSocket::OnDisconnect() +{ + mConsumer->OnDisconnect(mIndex); +} + +} // namespace ipc +} // namespace mozilla diff --git a/ipc/ril/RilSocket.h b/ipc/ril/RilSocket.h new file mode 100644 index 000000000000..e1ca8f75cd8c --- /dev/null +++ b/ipc/ril/RilSocket.h @@ -0,0 +1,110 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ipc_RilSocket_h +#define mozilla_ipc_RilSocket_h + +#include "mozilla/ipc/ConnectionOrientedSocket.h" + +class JSContext; +class MessageLoop; + +namespace mozilla { +namespace dom { +namespace workers { + +class WorkerCrossThreadDispatcher; + +} // namespace workers +} // namespace dom +} // namespace mozilla + +namespace mozilla { +namespace ipc { + +class RilSocketConsumer; +class RilSocketIO; +class UnixSocketConnector; + +class RilSocket final : public ConnectionOrientedSocket +{ +public: + /** + * Constructs an instance of |RilSocket|. + * + * @param aDispatcher The dispatcher class for the received messages. + * @param aConsumer The consumer for the socket. + * @param aIndex An arbitrary index. + */ + RilSocket(mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher, + RilSocketConsumer* aConsumer, int aIndex); + + /** + * Method to be called whenever data is received. RIL-worker only. + * + * @param aCx The RIL worker's JS context. + * @param aBuffer Data received from the socket. + */ + void ReceiveSocketData(JSContext* aCx, nsAutoPtr& aBuffer); + + /** + * Starts a task on the socket that will try to connect to a socket in a + * non-blocking manner. + * + * @param aConnector Connector object for socket type specific functions + * @param aDelayMs Time delay in milliseconds. + * @param aConsumerLoop The socket's consumer thread. + * @param aIOLoop The socket's I/O thread. + * @return NS_OK on success, or an XPCOM error code otherwise. + */ + nsresult Connect(UnixSocketConnector* aConnector, int aDelayMs, + MessageLoop* aConsumerLoop, MessageLoop* aIOLoop); + + /** + * Starts a task on the socket that will try to connect to a socket in a + * non-blocking manner. + * + * @param aConnector Connector object for socket type specific functions + * @param aDelayMs Time delay in milliseconds. + * @return NS_OK on success, or an XPCOM error code otherwise. + */ + nsresult Connect(UnixSocketConnector* aConnector, int aDelayMs = 0); + + // Methods for |ConnectionOrientedSocket| + // + + nsresult PrepareAccept(UnixSocketConnector* aConnector, + MessageLoop* aConsumerLoop, + MessageLoop* aIOLoop, + ConnectionOrientedSocketIO*& aIO) override; + + // Methods for |DataSocket| + // + + void SendSocketData(UnixSocketIOBuffer* aBuffer) override; + + // Methods for |SocketBase| + // + + void Close() override; + void OnConnectSuccess() override; + void OnConnectError() override; + void OnDisconnect() override; + +protected: + virtual ~RilSocket(); + +private: + RilSocketIO* mIO; + nsRefPtr mDispatcher; + RilSocketConsumer* mConsumer; + int mIndex; +}; + +} // namespace ipc +} // namepsace mozilla + +#endif // mozilla_ipc_RilSocket_h diff --git a/ipc/ril/RilSocketConsumer.cpp b/ipc/ril/RilSocketConsumer.cpp new file mode 100644 index 000000000000..a8a7418872f6 --- /dev/null +++ b/ipc/ril/RilSocketConsumer.cpp @@ -0,0 +1,20 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "RilSocketConsumer.h" + +namespace mozilla { +namespace ipc { + +// +// RilSocketConsumer +// + +RilSocketConsumer::~RilSocketConsumer() +{ } + +} +} diff --git a/ipc/ril/RilSocketConsumer.h b/ipc/ril/RilSocketConsumer.h new file mode 100644 index 000000000000..510517b4ad28 --- /dev/null +++ b/ipc/ril/RilSocketConsumer.h @@ -0,0 +1,64 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ipc_RilSocketConsumer_h +#define mozilla_ipc_RilSocketConsumer_h + +#include "nsAutoPtr.h" + +class JSContext; + +namespace mozilla { +namespace ipc { + +class UnixSocketBuffer; + +/** + * |RilSocketConsumer| handles socket events and received data. + */ +class RilSocketConsumer +{ +public: + /** + * Method to be called whenever data is received. RIL-worker only. + * + * @param aCx The RIL worker's JS context. + * @param aIndex The index that has been given to the stream socket. + * @param aBuffer Data received from the socket. + */ + virtual void ReceiveSocketData(JSContext* aCx, + int aIndex, + nsAutoPtr& aBuffer) = 0; + + /** + * Callback for socket success. Consumer-thread only. + * + * @param aIndex The index that has been given to the stream socket. + */ + virtual void OnConnectSuccess(int aIndex) = 0; + + /** + * Callback for socket errors. Consumer-thread only. + * + * @param aIndex The index that has been given to the stream socket. + */ + virtual void OnConnectError(int aIndex) = 0; + + /** + * Callback for socket disconnect. Consumer-thread only. + * + * @param aIndex The index that has been given to the stream socket. + */ + virtual void OnDisconnect(int aIndex) = 0; + +protected: + virtual ~RilSocketConsumer(); +}; + +} +} + +#endif diff --git a/ipc/ril/moz.build b/ipc/ril/moz.build index 4e7018bcb6af..215d9e2ac624 100644 --- a/ipc/ril/moz.build +++ b/ipc/ril/moz.build @@ -6,11 +6,15 @@ EXPORTS.mozilla.ipc += [ 'Ril.h', + 'RilSocket.h', + 'RilSocketConsumer.h' ] SOURCES += [ 'Ril.cpp', - 'RilConnector.cpp' + 'RilConnector.cpp', + 'RilSocket.cpp', + 'RilSocketConsumer.cpp' ] include('/ipc/chromium/chromium-config.mozbuild')