/** * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * The Radio object contains a set of methods and objects to handle ril request * which is passed from simulatedRadioWorker queue. The global object initialize * an instance of Radio object by calling "new Radio". For each ril request, * rilDispatchTable gets searched and the corresponding method is called. * Extra requests are also defined to process unsolicited rerequests. * * The rilDispatchTable is an array indexed by RIL_REQUEST_* or REQUEST_UNSOL_*, * in which each request corresponds to a functions defined in the Radio object. * We need to pay attention when using "this" within those functions. When they are * called in "this.process" using * result = this.radioDispatchTable[req.reqNum])(req); * this scope of "this" within those functions are the radioDispatchTable, not the * object that "this.process" belongs to. Using "this." to access other * functions in the object may cause trouble. * To avoid that, the object is passed in when those functions are called as * shown in the following: * result = (this.radioDispatchTable[req.reqNum]).call(this, req); */ /** * Set radio state */ function setRadioState(newState) { newRadioState = newState; if (typeof newState == 'string') { newRadioState = globals[newState]; if (typeof newRadioState == 'undefined') { throw('setRadioState: Unknow string: ' + newState); } } if ((newRadioState < RADIOSTATE_OFF) || (newRadioState > RADIOSTATE_NV_READY)) { throw('setRadioState: newRadioState: ' + newRadioState + ' is invalid'); } gRadioState = newRadioState; sendRilUnsolicitedResponse(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED); } /** * Create a call. * * @return a RilCall */ function RilCall(state, phoneNumber, callerName) { this.state = state; this.index = 0; this.toa = 0; this.isMpty = false; this.isMt = false; this.als = 0; this.isVoice = true; this.isVoicePrivacy = false; this.number = phoneNumber; this.numberPresentation = 0; this.name = callerName; } /** * Simulated Radio */ function Radio() { var registrationState = '1'; var lac = '0'; var cid = '0'; var radioTechnology = '3'; var baseStationId = NULL_RESPONSE_STRING; var baseStationLatitude = NULL_RESPONSE_STRING; var baseStationLongitude = NULL_RESPONSE_STRING; var concurrentServices = NULL_RESPONSE_STRING; var systemId = NULL_RESPONSE_STRING; var networkId = NULL_RESPONSE_STRING; var roamingIndicator = NULL_RESPONSE_STRING; var prlActive = NULL_RESPONSE_STRING; var defaultRoamingIndicator = NULL_RESPONSE_STRING; var registrationDeniedReason = NULL_RESPONSE_STRING; var primaryScrambingCode = NULL_RESPONSE_STRING; var NETWORK_SELECTION_MODE_AUTOMATIC = 0; var NETWORK_SELECTION_MODE_MANUAL = 1; var networkSelectionMode = NETWORK_SELECTION_MODE_AUTOMATIC; var muteState = 0; // disable mute // Number of active calls in calls var numberActiveCalls = 0; // Maximum number of active calls var maxNumberActiveCalls = 7; var maxConnectionsPerCall = 5; // only 5 connections allowed per call // Flag to denote whether an incoming/waiting call is answered var incomingCallIsProcessed = false; // Call transition flag var callTransitionFlag = false; // default to auto-transition var lastCallFailCause = 0; // Array of "active" calls var calls = Array(maxNumberActiveCalls + 1); // The result returned by the request handlers var result = new Object(); function GWSignalStrength() { this.signalStrength = 10; // 10 * 2 + (-113) = -93dBm, make it three bars this.bitErrorRate = 0; } function CDMASignalStrength() { this.dbm = -1; this.ecio = -1; } function EVDOSignalStrength() { this.dbm = -1; this.ecio = -1; this.signalNoiseRatio = 0; } function LTESignalStrength() { this.signalStrength = -1; this.rsrp = 0; this.rsrq = 0; this.rssnr = 0; this.cqi = 0; } var gwSignalStrength = new GWSignalStrength; var cdmaSignalStrength = new CDMASignalStrength(); var evdoSignalStrength = new EVDOSignalStrength(); var lteSignalStrength = new LTESignalStrength(); /** * The the array of calls, this is a sparse * array and some elements maybe 'undefined'. * * @return Array of RilCall's */ this.getCalls = function() { return calls; } /** * @return the RilCall at calls[index] or null if undefined. */ this.getCall = function(index) { var c = null; try { c = calls[index]; if (typeof c == 'undefined') { c = null; } } catch (err) { c = null; } return c; } /** * @return the first call that is in the given state */ this.getCallIdByState = function(callState) { if ((callState < CALLSTATE_ACTIVE) || (callState > CALLSTATE_WAITING)) { return null; } for (var i = 0; i < calls.length; i++) { if (typeof calls[i] != 'undefined') { if (calls[i].state == callState) { return i; } } } return null; } /** Add an active call * * @return a RilCall or null if too many active calls. */ this.addCall = function(state, phoneNumber, callerName) { print('Radio: addCall'); var c = null; if (numberActiveCalls < maxNumberActiveCalls) { numberActiveCalls += 1; c = new RilCall(state, phoneNumber, callerName); // call index should fall in the closure of [1, 7] // Search for an "undefined" element in the array and insert the call for (var i = 1; i < (maxNumberActiveCalls + 1); i++) { print('Radio: addCall, i=' + i); if (typeof calls[i] == 'undefined') { print('Radio: addCall, calls[' + i + '] is undefined'); c.index = i; calls[i] = c; break; } } this.printCalls(calls); } return c; } /** * Remove the call, does nothing if the call is undefined. * * @param index into calls to remove. * @return the call removed or null if did not exist */ this.removeCall = function(index) { var c = null; if ((numberActiveCalls > 0) && (index < calls.length) && (typeof calls[index] != 'undefined')) { c = calls[index]; delete calls[index]; numberActiveCalls -= 1; if (numberActiveCalls == 0) { calls = new Array(); } } else { c = null; } return c; } /** * Print the call * * @param c is the RilCall to print */ this.printCall = function(c) { if ((c != null) && (typeof c != 'undefined')) { print('c[' + c.index + ']: index=' + c.index + ' state=' + c.state + ' number=' + c.number + ' name=' + c.name); } } /** * Print all the calls. * * @param callArray is an Array of RilCall's */ this.printCalls = function(callArray) { if (typeof callArray == 'undefined') { callArray = calls; } print('callArray.length=' + callArray.length); for (var i = 0; i < callArray.length; i++) { if ((callArray[i] == null) || (typeof callArray[i] == 'undefined')) { print('c[' + i + ']: undefined'); } else { this.printCall(callArray[i]); } } } /** * Count number of calls in the given state * * @param callState is the give state */ this.countCallsInState = function(callState) { var count = 0; if ((callState < CALLSTATE_ACTIVE) || (callState > CALLSTATE_WAITING)) { // not a valid call state return null; } for (var i = 0; i < calls.length; i++) { if (typeof calls[i] != 'undefined') { if (calls[i].state == callState) { count++; } } } return count; } /** * Print signal strength */ this.printSignalStrength = function() { print('rssi: ' + gwSignalStrength.signalStrength); print('bitErrorRate: ' + gwSignalStrength.bitErrorRate); print('cdmaDbm: ' + cdmaSignalStrength.dbm); print('cdmaEcio: ' + cdmaSignalStrength.ecio); print('evdoRssi: ' + evdoSignalStrength.dbm); print('evdoEcio: ' + evdoSignalStrength.ecio); print('evdoSnr: ' + evdoSignalStrength.signalNoiseRatio); print('lteRssi: ' + lteSignalStrength.signalStrength); print('lteRsrp: ' + lteSignalStrength.rsrp); print('lteRsrq: ' + lteSignalStrength.rsrq); print('lteRssnr: ' + lteSignalStrength.rssnr); print('lteCqi: ' + lteSignalStrength.cqi); } /** * set signal strength * * @param rssi and bitErrorRate are signal strength parameters for GSM * cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, evdoSnr are parameters for CDMA & EVDO */ this.setSignalStrength = function(rssi, bitErrorRate, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, evdoSnr, lteSigStrength, lteRsrp, lteRsrq, lteRssnr, lteCqi) { print('setSignalStrength E'); if (rssi != 99) { if ((rssi < 0) || (rssi > 31)) { throw ('not a valid signal strength'); } } // update signal strength gwSignalStrength.signalStrength = rssi; gwSignalStrength.bitErrorRate = bitErrorRate; cdmaSignalStrength.dbm = cdmaDbm; cdmaSignalStrength.ecio = cdmaEcio; evdoSignalStrength.dbm = evdoRssi; evdoSignalStrength.ecio = evdoEcio; evdoSignalStrength.signalNoiseRatio = evdoSnr; lteSignalStrength.signalStrength = lteSigStrength; lteSignalStrength.rsrp = lteRsrp; lteSignalStrength.rsrq = lteRsrq; lteSignalStrength.rssnr = lteRssnr; lteSignalStrength.cqi = lteCqi; // pack the signal strength into RspSignalStrength and send a unsolicited response var rsp = new Object(); rsp.gwSignalstrength = gwSignalStrength; rsp.cdmSignalstrength = cdmaSignalStrength; rsp.evdoSignalstrength = evdoSignalStrength; rsp.lteSignalstrength = lteSignalStrength; var response = rilSchema[packageNameAndSeperator + 'RspSignalStrength'].serialize(rsp); sendRilUnsolicitedResponse(RIL_UNSOL_SIGNAL_STRENGTH, response); // send the unsolicited signal strength every 1 minute. simulatedRadioWorker.addDelayed( {'reqNum' : CMD_UNSOL_SIGNAL_STRENGTH}, 60000); print('setSignalStrength X'); } /** * Handle RIL_REQUEST_GET_CURRENT_CALL * * @param req is the Request */ this.rilRequestGetCurrentCalls = function(req) { // 9 print('Radio: rilRequestGetCurrentCalls E'); var rsp = new Object(); // pack calls into rsp.calls rsp.calls = new Array(); var i; var j; for (i = 0, j = 0; i < calls.length; i++) { if (typeof calls[i] != 'undefined') { rsp.calls[j++] = calls[i]; } } result.responseProtobuf = rilSchema[packageNameAndSeperator + 'RspGetCurrentCalls'].serialize(rsp); return result; } /** * Handle RIL_REQUEST_DIAL * * @param req is the Request */ this.rilRequestDial = function(req) { // 10 print('Radio: rilRequestDial E'); var newCall = new Object(); newCall = this.addCall(CALLSTATE_DIALING, req.data.address, ''); if (newCall == null) { result.rilErrCode = RIL_E_GENERIC_FAILURE; return result; } this.printCalls(calls); print('after add the call'); // Set call state to dialing simulatedRadioWorker.add( {'reqNum' : CMD_CALL_STATE_CHANGE, 'callType' : OUTGOING, 'callIndex' : newCall.index, 'nextState' : CALLSTATE_DIALING}); if (!callTransitionFlag) { // for auto transition // Update call state to alerting after 1 second simulatedRadioWorker.addDelayed( {'reqNum' : CMD_CALL_STATE_CHANGE, 'callType' : OUTGOING, 'callIndex' : newCall.index, 'nextState' : CALLSTATE_ALERTING}, 1000); // Update call state to active after 2 seconds simulatedRadioWorker.addDelayed( {'reqNum' : CMD_CALL_STATE_CHANGE, 'callType' : OUTGOING, 'callIndex': newCall.index, 'nextState' : CALLSTATE_ACTIVE}, 2000); } return result; } /** * Handle RIL_REQUEST_HANG_UP * * @param req is the Request */ this.rilRequestHangUp = function(req) { // 12 print('Radio: rilRequestHangUp data.connection_index=' + req.data.connectionIndex); if (this.removeCall(req.data.connectionIndex) == null) { result.rilErrCode = RIL_E_GENERIC_FAILURE; print('no connection to hangup'); } return result; } /** * Handle RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND * Hang up waiting or held * * @param req is the Request */ this.rilRequestHangupWaitingOrBackground = function(req) { // 13 print('Radio: rilRequestHangupWaitingOrBackground'); if (numberActiveCalls <= 0) { result.rilErrCode = RIL_E_GENERIC_FAILURE; } else { for (var i = 0; i < calls.length; i++) { if (typeof calls[i] != 'undefined') { switch (calls[i].state) { case CALLSTATE_HOLDING: case CALLSTATE_WAITING: case CALLSTATE_INCOMING: this.removeCall(i); incomingCallIsProcessed = true; break; default: result.rilErrCode = RIL_E_GENERIC_FAILURE; break; } this.printCalls(calls); if(result.rilErrCode == RIL_E_GENERIC_FAILURE) { return result; } } // end of processing call[i] } // end of for } // Send out RIL_UNSOL_CALL_STATE_CHANGED after the request is returned simulatedRadioWorker.add( {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED}); return result; } /** * Handle RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND * release all active calls (if any exist) and resume held or waiting calls. * @param req is the Request */ this.rilRequestHangUpForegroundResumeBackground = function(req) { //14 print('Radio: rilRequestHangUpForegroundResumeBackground'); if (numberActiveCalls <= 0) { result.rilErrCode = RIL_E_GENERIC_FAILURE; } else { for (var i = 0; i < calls.length; i++) { if (typeof calls[i] != 'undefined') { switch (calls[i].state) { case CALLSTATE_ACTIVE: this.removeCall(i); break; case CALLSTATE_HOLDING: case CALLSTATE_WAITING: calls[i].state = CALLSTATE_ACTIVE; break; default: result.rilErrCode = RIL_E_GENERIC_FAILURE; break; } this.printCalls(calls); if(result.rilErrCode == RIL_E_GENERIC_FAILURE) { return result; } } // end of processing call[i] } } // Send out RIL_UNSOL_CALL_STATE_CHANGED after the request is returned simulatedRadioWorker.add( {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED}); return result; } /** * Handle RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE * * BEFORE AFTER * Call 1 Call 2 Call 1 Call 2 * ACTIVE HOLDING HOLDING ACTIVE * ACTIVE WAITING HOLDING ACTIVE * HOLDING WAITING HOLDING ACTIVE * ACTIVE IDLE HOLDING IDLE * IDLE IDLE IDLE IDLE * * @param req is the Request */ this.rilRequestSwitchWaitingOrHoldingAndActive = function(req) { // 15 print('Radio: rilRequestSwitchWaitingOrHoldingAndActive'); print('Radio: lastReq = ' + lastReq); print('Radio: req.reqNum = ' + req.reqNum); if (lastReq == req.reqNum) { print('Radio: called twice'); return result; } if (numberActiveCalls <= 0) { result.rilErrCode = RIL_E_GENERIC_FAILURE; } else { for (var i = 0; i < calls.length; i++) { if (typeof calls[i] != 'undefined') { switch (calls[i].state) { case CALLSTATE_ACTIVE: calls[i].state = CALLSTATE_HOLDING; break; case CALLSTATE_HOLDING: case CALLSTATE_WAITING: calls[i].state = CALLSTATE_ACTIVE; break; default: result.rilErrCode = RIL_E_GENERIC_FAILURE; break; } this.printCalls(calls); if(result.rilErrCode == RIL_E_GENERIC_FAILURE) { return result; } } // end of processing call[i] } // end of for } // Send out RIL_UNSOL_CALL_STATE_CHANGED after the request is returned simulatedRadioWorker.add( {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED}); return result; } /** * Handle RIL_REQUEST_CONFERENCE * Conference holding and active * * @param req is the Request */ this.rilRequestConference = function(req) { // 16 print('Radio: rilRequestConference E'); if ((numberActiveCalls <= 0) || (numberActiveCalls > maxConnectionsPerCall)) { // The maximum number of connections within a call is 5 result.rilErrCode = RIL_E_GENERIC_FAILURE; return result; } else { var numCallsInBadState = 0; for (var i = 0; i < calls.length; i++) { if (typeof calls[i] != 'undefined') { if ((calls[i].state != CALLSTATE_ACTIVE) && (calls[i].state != CALLSTATE_HOLDING)) { numCallsInBadState++; } } } // if there are calls not in ACITVE or HOLDING state, return error if (numCallsInBadState > 0) { result.rilErrCode = RIL_E_GENERIC_FAILURE; return result; } else { // conference ACTIVE and HOLDING calls for (var i = 0; i < calls.length; i++) { if (typeof calls[i] != 'undefined') { switch (calls[i].state) { case CALLSTATE_ACTIVE: break; case CALLSTATE_HOLDING: calls[i].state = CALLSTATE_ACTIVE; break; default: result.rilErrCode = RIL_E_GENERIC_FAILURE; break; } } this.printCalls(calls); if(result.rilErrCode == RIL_E_GENERIC_FAILURE) { return result; } } } } // Only if conferencing is successful, // Send out RIL_UNSOL_CALL_STATE_CHANGED after the request is returned simulatedRadioWorker.add( {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED}); return result; } /** * Handle RIL_REQUEST_LAST_CALL_FAIL_CAUSE * * @param req is the request */ this.rilRequestLastCallFailCause = function(req) { print('Radio: rilRequestLastCallFailCause E'); var rsp = new Object(); rsp.integers = new Array(); rsp.integers[0] = lastCallFailCause; result.responseProtobuf = rilSchema[packageNameAndSeperator + 'RspIntegers'].serialize(rsp); return result; } /** * Handle RIL_REQUEST_SIGNAL_STRENGTH * * @param req is the Request */ this.rilRequestSignalStrength = function(req) { // 19 print('Radio: rilRequestSignalStrength E'); var rsp = new Object(); // pack the signal strength into RspSignalStrength rsp.gwSignalstrength = gwSignalStrength; rsp.cdmaSignalstrength = cdmaSignalStrength; rsp.evdoSignalstrength = evdoSignalStrength; result.responseProtobuf = rilSchema[packageNameAndSeperator + 'RspSignalStrength'].serialize(rsp); return result; } /** * Handle RIL_REQUEST_VOICE_REGISTRATION_STATE * * @param req is the Request */ this.rilRequestVoiceRegistrationState = function(req) { // 20 print('Radio: rilRequestVoiceRegistrationState'); var rsp = new Object(); rsp.strings = Array(); rsp.strings[0] = registrationState; rsp.strings[1] = lac; rsp.strings[2] = cid; rsp.strings[3] = radioTechnology; rsp.strings[4] = baseStationId; rsp.strings[5] = baseStationLatitude; rsp.strings[6] = baseStationLongitude; rsp.strings[7] = concurrentServices; rsp.strings[8] = systemId; rsp.strings[9] = networkId; rsp.strings[10] = roamingIndicator; rsp.strings[11] = prlActive; rsp.strings[12] = defaultRoamingIndicator; rsp.strings[13] = registrationDeniedReason; rsp.strings[14] = primaryScrambingCode; result.responseProtobuf = rilSchema[packageNameAndSeperator + 'RspStrings'].serialize(rsp); return result; } /** * Handle RIL_REQUEST_DATA_REGISTRATION_STATE * * @param req is the Request */ this.rilRequestDataRegistrationState = function(req) { // 21 print('Radio: rilRequestDataRegistrationState'); var rsp = new Object(); rsp.strings = Array(); rsp.strings[0] = registrationState; rsp.strings[1] = lac; rsp.strings[2] = cid; rsp.strings[3] = radioTechnology; result.responseProtobuf = rilSchema[packageNameAndSeperator + 'RspStrings'].serialize(rsp); return result; } /** * Handle RIL_REQUEST_ANSWER * * @param req is the Request */ this.rilRequestAnswer = function(req) { // 40 print('Radio: rilRequestAnswer'); if (numberActiveCalls != 1) { result.rilErrCode = RIL_E_GENERIC_FAILURE; return result; } else { for (var i = 0; i < calls.length; i++) { if (typeof calls[i] != 'undefined') { if (calls[i].state == CALLSTATE_INCOMING) { calls[i].state = CALLSTATE_ACTIVE; break; } else { result.rilErrCode = RIL_E_GENERIC_FAILURE; this.removeCall(i); return result; } } // end of processing call[i] } // end of for } incomingCallIsProcessed = true; return result; } /** * Handle RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE * * @param req is the Request */ this.rilRequestQueryNeworkSelectionMode = function(req) { // 45 print('Radio: rilRequestQueryNeworkSelectionMode'); var rsp = new Object(); rsp.integers = Array(); rsp.integers[0] = networkSelectionMode; result.responseProtobuf = rilSchema[packageNameAndSeperator + 'RspIntegers'].serialize(rsp); return result; } /** * Handle RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC * * @param req is the Request */ this.rilRequestSetNeworkSelectionAutomatic = function(req) { // 46 print('Radio: rilRequestSetNeworkSelectionAutomatic'); networkSelectionMode = NETWORK_SELECTION_MODE_AUTOMATIC; return result; } /** * Handle RIL_REQUEST_BASE_BAND_VERSION * * @param req is the Request */ this.rilRequestBaseBandVersion = function(req) { // 51 print('Radio: rilRequestBaseBandVersion'); var rsp = new Object(); rsp.strings = Array(); rsp.strings[0] = gBaseBandVersion; result.responseProtobuf = rilSchema[packageNameAndSeperator + 'RspStrings'].serialize(rsp); return result; } /** * Handle RIL_REQUEST_SEPRATE_CONNECTION * Separate a party from a multiparty call placing the multiparty call * (less the specified party) on hold and leaving the specified party * as the only other member of the current (active) call * * See TS 22.084 1.3.8.2 (iii) * * @param req is the Request */ this.rilReqestSeparateConnection = function(req) { // 52 print('Radio: rilReqestSeparateConnection'); var index = req.data.index; if (numberActiveCalls <= 0) { result.rilErrCode = RIL_E_GENERIC_FAILURE; return result; } else { // get the connection to separate var separateConn = this.getCall(req.data.index); if (separateConn == null) { result.rilErrCode = RIL_E_GENERIC_FAILURE; return result; } else { if (separateConn.state != CALLSTATE_ACTIVE) { result.rilErrCode = RIL_E_GENERIC_FAILURE; return result; } // Put all other connections in hold. for (var i = 0; i < calls.length; i++) { if (index != i) { // put all the active call to hold if (typeof calls[i] != 'undefined') { switch (calls[i].state) { case CALLSTATE_ACTIVE: calls[i].state = CALLSTATE_HOLDING; break; default: result.rilErrCode = RIL_E_GENERIC_FAILURE; break; } this.printCalls(calls); if(result.rilErrCode == RIL_E_GENERIC_FAILURE) { return result; } } // end of processing call[i] } } // end of for } } // Send out RIL_UNSOL_CALL_STATE_CHANGED after the request is returned simulatedRadioWorker.add( {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED}); return result; } /** * Handle RIL_REQUEST_SET_MUTE */ this.rilRequestSetMute = function(req) { // 53 print('Radio: rilRequestSetMute: req.data.state=' + req.data.state); muteState = req.data.state; if (gRadioState == RADIOSTATE_UNAVAILABLE) { result.rilErrCode = RIL_E_RADIO_NOT_AVAILABLE; } return result; } /** * Handle RIL_REQUEST_SCREEN_STATE * * @param req is the Request */ this.rilRequestScreenState = function(req) { // 61 print('Radio: rilRequestScreenState: req.data.state=' + req.data.state); screenState = req.data.state; return result; } /** * Delay test */ this.cmdDelayTest = function(req) { // 2000 print('cmdDelayTest: req.hello=' + req.hello); result.sendResponse = false; return result; } /** * Delay for RIL_UNSOL_SIGNAL_STRENGTH * TODO: Simulate signal strength changes: * Method 1: provide an array for signal strength, and send the unsolicited * reponse periodically (the period can also be simulated) * Method 2: Simulate signal strength randomly (within a certain range) and * send the response periodically. */ this.cmdUnsolSignalStrength = function(req) { // 2001 print('cmdUnsolSignalStrength: req.reqNum=' + req.reqNum); var rsp = new Object(); // pack the signal strength into RspSignalStrength rsp.gwSignalstrength = gwSignalStrength; rsp.cdmaSignalstrength = cdmaSignalStrength; rsp.evdoSignalstrength = evdoSignalStrength; response = rilSchema[packageNameAndSeperator + 'RspSignalStrength'].serialize(rsp); // upldate signal strength sendRilUnsolicitedResponse(RIL_UNSOL_SIGNAL_STRENGTH, response); // Send the unsolicited signal strength every 1 minute. simulatedRadioWorker.addDelayed( {'reqNum' : CMD_UNSOL_SIGNAL_STRENGTH}, 60000); // this is not a request, no response is needed result.sendResponse = false; return result; } /** * Send RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED */ this.cmdUnsolCallStateChanged = function(req) { // 2002 print('cmdUnsolCallStateChanged: req.reqNum=' + req.reqNum); sendRilUnsolicitedResponse(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED); result.sendResponse = false; return result; } /** * Simulate call state change */ this.cmdCallStateChange = function(req) { // 2003 print('cmdCallStateChange: req.reqNum=' + req.reqNum); print('cmdCallStateChange: req.callType=' + req.callType); print('cmdCallStateChange: req.callIndex=' + req.callIndex); print('cmdCallStateChange: req.nextState=' + req.nextState); // if it is an outgoing call, update the call state of the call // Send out call state changed flag var curCall = this.getCall(req.callIndex); this.printCall(curCall); if (curCall != null) { curCall.state = req.nextState; } else { this.removeCall(req.callIndex); } // TODO: if it is an incoming call, update the call state of the call // Send out call state change flag // Send out RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED simulatedRadioWorker.add( {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED}); result.sendResponse = false; return result; } /** * send UNSOL_CALL_STATE_CHANGED and UNSOL_CALL_RING */ this.cmdUnsolCallRing = function(req) { // 2004 print('cmdUnsolCallRing: req.reqNum=' + req.reqNum); if(!incomingCallIsProcessed) { sendRilUnsolicitedResponse(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED); sendRilUnsolicitedResponse(RIL_UNSOL_CALL_RING); // Send the next alert in 3 seconds. [refer to ril.h definition] simulatedRadioWorker.addDelayed( {'reqNum' : CMD_UNSOL_CALL_RING}, 3000); } result.sendResponse = false; return result; } /** * Create an incoming call for the giving number * return CTRL_STATUS_ERR if there is already a call in any of the states of * dialing, alerting, incoming, waiting [TS 22 030 6.5] , else * return CTRL_STATUS_OK and update the call state */ this.ctrlServerCmdStartInComingCall = function(req) { // 1001 print('ctrlServerCmdStartInComingCall: req.reqNum:' + req.reqNum); print('ctrlServerCmdStartInComingCall: req.data.phonenumber:' + req.data.phoneNumber); var phoneNumber = req.data.phoneNumber; var state; if (numberActiveCalls <= 0) { // If there is no connection in use, the call state is INCOMING state = CALLSTATE_INCOMING; } else { // If there is call in any of the states of dialing, alerting, incoming // waiting, this MT call can not be set for (var i = 0; i < calls.length; i++) { if (typeof calls[i] != 'undefined') { if ( (calls[i].state == CALLSTATE_DIALING) || (calls[i].state == CALLSTATE_ALERTING) || (calls[i].state == CALLSTATE_INCOMING) || (calls[i].state == CALLSTATE_WAITING)) { result.rilErrCode = CTRL_STATUS_ERR; return result; } } } // If the incoming call is a second call, the state is WAITING state = CALLSTATE_WAITING; } // Add call to the call array this.addCall(state, phoneNumber, ''); // set the incomingCallIsProcessed flag to be false incomingCallIsProcessed = false; simulatedRadioWorker.add( {'reqNum' : CMD_UNSOL_CALL_RING}); result.rilErrCode = CTRL_STATUS_OK; return result; } /** * Handle control command CTRL_CMD_HANGUP_CONN_REMOTE * hangup the connection for the given failure cause * *@param req is the control request */ this.ctrlServerCmdHangupConnRemote = function(req) { // 1002 print('ctrlServerCmdHangupConnRemote: req.data.connectionId=' + req.data.connectionId + ' req.data.callFailCause' + req.data.callFailCause); var connection = req.data.connectionId; var failureCause = req.data.callFailCause; this.printCalls(calls); var hangupCall = this.removeCall(connection); if (hangupCall == null) { print('ctrlServerCmdHangupConnRemote: connection id is required.'); result.rilErrCode = CTRL_STATUS_ERR; return result; } else { // for incoming call, stop sending call ring if ((hangupCall.state == CALLSTATE_INCOMING) || (hangupCall.state == CALLSTATE_WAITING)) { incomingCallIsProcessed = true; } } this.printCalls(calls); lastCallFailCause = failureCause; // send out call state changed response simulatedRadioWorker.add( {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED}); result.rilErrCode = CTRL_STATUS_OK; return result; } /** * Set call transition flag */ this.ctrlServerCmdSetCallTransitionFlag = function(req) { // 1003 print('ctrlServerCmdSetCallTransitionFlag: flag=' + req.data.flag); callTransitionFlag = req.data.flag; result.rilErrCode = CTRL_STATUS_OK; return result; } /** * Set the dialing call to alert */ this.ctrlServerCmdSetCallAlert = function(req) { // 1004 print('ctrlServerCmdSetCallAlert: E'); if (callTransitionFlag == false) { print('ctrlServerCmdSetCallAlert: need to set the flag first'); result.rilErrCode = CTRL_STATUS_ERR; return result; } if (numberActiveCalls <= 0) { print('ctrlServerCmdSetCallAlert: no active calls'); result.rilErrCode = CTRL_STATUS_ERR; return result; } var dialingCalls = this.countCallsInState(CALLSTATE_DIALING); var index = this.getCallIdByState(CALLSTATE_DIALING); if ((dialingCalls == 1) && (index != null)) { calls[index].state = CALLSTATE_ALERTING; } else { // if there 0 or more than one call in dialing state, return error print('ctrlServerCmdSetCallAlert: no valid calls in dialing state'); result.rilErrCode = CTRL_STATUS_ERR; return result; } // send unsolicited call state change response simulatedRadioWorker.add( {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED}); result.rilErrCode = CTRL_STATUS_OK; return result; } /** * Set the alserting call to active */ this.ctrlServerCmdSetCallActive = function(req) { // 1005 print('ctrlServerCmdSetCallActive: E'); if (callTransitionFlag == false) { print('ctrlServerCmdSetCallActive: need to set the flag firt'); result.rilErrCode = CTRL_STATUS_ERR; return result; } if (numberActiveCalls <= 0) { print('ctrlServerCmdSetCallActive: no active calls'); result.rilErrCode = CTRL_STATUS_ERR; return result; } var alertingCalls = this.countCallsInState(CALLSTATE_ALERTING); var index = this.getCallIdByState(CALLSTATE_ALERTING); if ((alertingCalls == 1) && (index != null)) { calls[index].state = CALLSTATE_ACTIVE; } else { print('ctrlServerCmdSetCallActive: no valid calls in alert state'); result.rilErrCode = CTRL_STATUS_ERR; return result; } // send out unsolicited call state change response simulatedRadioWorker.add( {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED}); result.rilErrCode = CTRL_STATUS_OK; return result; } /** * Add a dialing call */ this.ctrlServerCmdAddDialingCall = function(req) { // 1006 print('ctrlServerCmdAddDialingCall: E'); var phoneNumber = req.data.phoneNumber; var dialingCalls = this.countCallsInState(CALLSTATE_DIALING); if (dialingCalls == 0) { this.addCall(CALLSTATE_DIALING, phoneNumber, ''); result.rilErrCode = CTRL_STATUS_OK; } else { print('ctrlServerCmdAddDialingCall: add dialing call failed'); result.rilErrCode = CTRL_STATUS_ERR; } return result; } /** * Process the request by dispatching to the request handlers */ this.process = function(req) { try { print('Radio E: req.reqNum=' + req.reqNum + ' req.token=' + req.token); // Assume the result will be true, successful and nothing to return result.sendResponse = true; result.rilErrCode = RIL_E_SUCCESS; result.responseProtobuf = emptyProtobuf; try { // Pass "this" object to each ril request call such that // they have the same scope result = (this.radioDispatchTable[req.reqNum]).call(this, req); } catch (err) { print('Radio:process err = ' + err); print('Radio: Unknown reqNum=' + req.reqNum); result.rilErrCode = RIL_E_REQUEST_NOT_SUPPORTED; } if (req.reqNum < 200) { lastReq = req.reqNum; } if (result.sendResponse) { if (isCtrlServerDispatchCommand(req.reqNum)) { //print('Command ' + req.reqNum + ' is a control server command'); sendCtrlRequestComplete(result.rilErrCode, req.reqNum, req.token, result.responseProtobuf); } else { //print('Request ' + req.reqNum + ' is a ril request'); sendRilRequestComplete(result.rilErrCode, req.reqNum, req.token, result.responseProtobuf); } } //print('Radio X: req.reqNum=' + req.reqNum + ' req.token=' + req.token); } catch (err) { print('Radio: Exception req.reqNum=' + req.reqNum + ' req.token=' + req.token + ' err=' + err); } } /** * Construct the simulated radio */ print('Radio: constructor E'); this.radioDispatchTable = new Array(); this.radioDispatchTable[RIL_REQUEST_GET_CURRENT_CALLS] = // 9 this.rilRequestGetCurrentCalls; this.radioDispatchTable[RIL_REQUEST_DIAL] = // 10 this.rilRequestDial; this.radioDispatchTable[RIL_REQUEST_HANGUP] = // 12 this.rilRequestHangUp; this.radioDispatchTable[RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND] = // 13 this.rilRequestHangupWaitingOrBackground; this.radioDispatchTable[RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND] = // 14 this.rilRequestHangUpForegroundResumeBackground; this.radioDispatchTable[RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] = // 15 this.rilRequestSwitchWaitingOrHoldingAndActive; this.radioDispatchTable[RIL_REQUEST_CONFERENCE] = // 16 this.rilRequestConference; this.radioDispatchTable[RIL_REQUEST_LAST_CALL_FAIL_CAUSE] = // 18 this.rilRequestLastCallFailCause; this.radioDispatchTable[RIL_REQUEST_SIGNAL_STRENGTH] = // 19 this.rilRequestSignalStrength; this.radioDispatchTable[RIL_REQUEST_VOICE_REGISTRATION_STATE] = // 20 this.rilRequestVoiceRegistrationState; this.radioDispatchTable[RIL_REQUEST_DATA_REGISTRATION_STATE] = // 21 this.rilRequestDataRegistrationState; this.radioDispatchTable[RIL_REQUEST_ANSWER] = // 40 this.rilRequestAnswer; this.radioDispatchTable[RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE] = // 45 this.rilRequestQueryNeworkSelectionMode; this.radioDispatchTable[RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC] = // 46 this.rilRequestSetNeworkSelectionAutomatic; this.radioDispatchTable[RIL_REQUEST_BASEBAND_VERSION] = // 51 this.rilRequestBaseBandVersion; this.radioDispatchTable[RIL_REQUEST_SEPARATE_CONNECTION] = // 52 this.rilReqestSeparateConnection; this.radioDispatchTable[RIL_REQUEST_SET_MUTE] = // 53 this.rilRequestSetMute; this.radioDispatchTable[RIL_REQUEST_SCREEN_STATE] = // 61 this.rilRequestScreenState; this.radioDispatchTable[CTRL_CMD_SET_MT_CALL] = //1001 this.ctrlServerCmdStartInComingCall; this.radioDispatchTable[CTRL_CMD_HANGUP_CONN_REMOTE] = //1002 this.ctrlServerCmdHangupConnRemote; this.radioDispatchTable[CTRL_CMD_SET_CALL_TRANSITION_FLAG] = //1003 this.ctrlServerCmdSetCallTransitionFlag; this.radioDispatchTable[CTRL_CMD_SET_CALL_ALERT] = // 1004 this.ctrlServerCmdSetCallAlert; this.radioDispatchTable[CTRL_CMD_SET_CALL_ACTIVE] = // 1005 this.ctrlServerCmdSetCallActive; this.radioDispatchTable[CTRL_CMD_ADD_DIALING_CALL] = // 1006 this.ctrlServerCmdAddDialingCall; this.radioDispatchTable[CMD_DELAY_TEST] = // 2000 this.cmdDelayTest; this.radioDispatchTable[CMD_UNSOL_SIGNAL_STRENGTH] = // 2001 this.cmdUnsolSignalStrength; this.radioDispatchTable[CMD_UNSOL_CALL_STATE_CHANGED] = // 2002 this.cmdUnsolCallStateChanged; this.radioDispatchTable[CMD_CALL_STATE_CHANGE] = //2003 this.cmdCallStateChange; this.radioDispatchTable[CMD_UNSOL_CALL_RING] = //2004 this.cmdUnsolCallRing; print('Radio: constructor X'); } // The simulated radio instance and its associated Worker var simulatedRadio = new Radio(); var simulatedRadioWorker = new Worker(function (req) { simulatedRadio.process(req); }); simulatedRadioWorker.run(); // TODO: this is a workaround for bug http://b/issue?id=3001613 // When adding a new all, two RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE // will be sent from the framework. var lastReq = 0; /** * Optional tests */ if (false) { include("simulated_radio_tests.js"); }