/**
* 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");
}