• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/**
18 * The Radio object contains a set of methods and objects to handle ril request
19 * which is passed from simulatedRadioWorker queue. The global object initialize
20 * an instance of Radio object by calling "new Radio". For each ril request,
21 * rilDispatchTable gets searched and the corresponding method is called.
22 * Extra requests are also defined to process unsolicited rerequests.
23 *
24 * The rilDispatchTable is an array indexed by RIL_REQUEST_* or REQUEST_UNSOL_*,
25 * in which each request corresponds to a functions defined in the Radio object.
26 * We need to pay attention when using "this" within those functions. When they are
27 * called in "this.process" using
28 *               result = this.radioDispatchTable[req.reqNum])(req);
29 * this scope of "this" within those functions are the radioDispatchTable, not the
30 * object that "this.process" belongs to. Using "this." to access other
31 * functions in the object may cause trouble.
32 * To avoid that, the object is passed in when those functions are called as
33 * shown in the following:
34 *                 result = (this.radioDispatchTable[req.reqNum]).call(this, req);
35 */
36
37/**
38 * Set radio state
39 */
40function setRadioState(newState) {
41    newRadioState = newState;
42    if (typeof newState == 'string') {
43        newRadioState = globals[newState];
44        if (typeof newRadioState == 'undefined') {
45            throw('setRadioState: Unknow string: ' + newState);
46        }
47    }
48    if ((newRadioState < RADIOSTATE_OFF) || (newRadioState > RADIOSTATE_NV_READY)) {
49        throw('setRadioState: newRadioState: ' + newRadioState + ' is invalid');
50    }
51    gRadioState = newRadioState;
52    sendRilUnsolicitedResponse(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED);
53}
54
55/**
56 * Create a call.
57 *
58 * @return a RilCall
59 */
60function RilCall(state, phoneNumber, callerName) {
61    this.state = state;
62    this.index = 0;
63    this.toa = 0;
64    this.isMpty = false;
65    this.isMt = false;
66    this.als = 0;
67    this.isVoice = true;
68    this.isVoicePrivacy = false;
69    this.number = phoneNumber;
70    this.numberPresentation = 0;
71    this.name = callerName;
72}
73
74/**
75 * Simulated Radio
76 */
77function Radio() {
78    var registrationState = '1';
79    var lac = '0';
80    var cid = '0';
81    var radioTechnology = '3';
82    var baseStationId = NULL_RESPONSE_STRING;
83    var baseStationLatitude = NULL_RESPONSE_STRING;
84    var baseStationLongitude = NULL_RESPONSE_STRING;
85    var concurrentServices = NULL_RESPONSE_STRING;
86    var systemId  = NULL_RESPONSE_STRING;
87    var networkId = NULL_RESPONSE_STRING;
88    var roamingIndicator = NULL_RESPONSE_STRING;
89    var prlActive = NULL_RESPONSE_STRING;
90    var defaultRoamingIndicator = NULL_RESPONSE_STRING;
91    var registrationDeniedReason = NULL_RESPONSE_STRING;
92    var primaryScrambingCode = NULL_RESPONSE_STRING;
93
94    var NETWORK_SELECTION_MODE_AUTOMATIC = 0;
95    var NETWORK_SELECTION_MODE_MANUAL = 1;
96    var networkSelectionMode = NETWORK_SELECTION_MODE_AUTOMATIC;
97
98    var muteState = 0;   // disable mute
99
100    // Number of active calls in calls
101    var numberActiveCalls = 0;
102
103    // Maximum number of active calls
104    var maxNumberActiveCalls = 7;
105    var maxConnectionsPerCall = 5; // only 5 connections allowed per call
106
107    // Flag to denote whether an incoming/waiting call is answered
108    var incomingCallIsProcessed = false;
109
110    // Call transition flag
111    var callTransitionFlag = false;  // default to auto-transition
112
113    var lastCallFailCause = 0;
114
115    // Array of "active" calls
116    var calls = Array(maxNumberActiveCalls + 1);
117
118    // The result returned by the request handlers
119    var result = new Object();
120
121    function GWSignalStrength() {
122        this.signalStrength = 10;  // 10 * 2 + (-113) = -93dBm, make it three bars
123        this.bitErrorRate = 0;
124    }
125
126    function CDMASignalStrength() {
127        this.dbm = -1;
128        this.ecio = -1;
129    }
130
131    function EVDOSignalStrength() {
132        this.dbm = -1;
133        this.ecio = -1;
134        this.signalNoiseRatio = 0;
135    }
136
137    var gwSignalStrength = new GWSignalStrength;
138    var cdmaSignalStrength = new CDMASignalStrength();
139    var evdoSignalStrength = new EVDOSignalStrength();
140
141    /**
142     * The the array of calls, this is a sparse
143     * array and some elements maybe 'undefined'.
144     *
145     * @return Array of RilCall's
146     */
147    this.getCalls =  function() {
148        return calls;
149    }
150
151    /**
152     * @return the RilCall at calls[index] or null if undefined.
153     */
154    this.getCall = function(index) {
155        var c = null;
156        try {
157            c = calls[index];
158            if (typeof c == 'undefined') {
159                c = null;
160            }
161        } catch (err) {
162            c = null;
163        }
164        return c;
165    }
166
167    /**
168     * @return the first call that is in the given state
169     */
170    this.getCallIdByState = function(callState) {
171        if ((callState < CALLSTATE_ACTIVE) || (callState > CALLSTATE_WAITING)) {
172            return null;
173        }
174        for (var i = 0; i < calls.length; i++) {
175            if (typeof calls[i] != 'undefined') {
176                if (calls[i].state == callState) {
177                    return i;
178                }
179            }
180        }
181        return null;
182    }
183
184    /** Add an active call
185     *
186     * @return a RilCall or null if too many active calls.
187     */
188    this.addCall = function(state, phoneNumber, callerName) {
189        print('Radio: addCall');
190        var c = null;
191        if (numberActiveCalls < maxNumberActiveCalls) {
192            numberActiveCalls += 1;
193            c = new RilCall(state, phoneNumber, callerName);
194            // call index should fall in the closure of [1, 7]
195            // Search for an "undefined" element in the array and insert the call
196            for (var i = 1; i < (maxNumberActiveCalls + 1); i++) {
197                print('Radio: addCall, i=' + i);
198                if (typeof calls[i] == 'undefined') {
199                    print('Radio: addCall, calls[' + i + '] is undefined');
200                    c.index = i;
201                    calls[i] = c;
202                    break;
203                }
204            }
205            this.printCalls(calls);
206        }
207        return c;
208    }
209
210    /**
211     * Remove the call, does nothing if the call is undefined.
212     *
213     * @param index into calls to remove.
214     * @return the call removed or null if did not exist
215     */
216    this.removeCall =  function(index) {
217        var c = null;
218        if ((numberActiveCalls > 0)
219                 && (index < calls.length)
220                 && (typeof calls[index] != 'undefined')) {
221            c = calls[index];
222            delete calls[index];
223            numberActiveCalls -= 1;
224            if (numberActiveCalls == 0) {
225                calls = new Array();
226            }
227        } else {
228            c = null;
229        }
230        return c;
231    }
232
233    /**
234     * Print the call
235     *
236     * @param c is the RilCall to print
237     */
238    this.printCall = function(c) {
239        if ((c != null) && (typeof c != 'undefined')) {
240            print('c[' + c.index + ']: index=' + c.index + ' state=' + c.state +
241                  ' number=' + c.number + ' name=' + c.name);
242        }
243    }
244
245    /**
246     * Print all the calls.
247     *
248     * @param callArray is an Array of RilCall's
249     */
250    this.printCalls = function(callArray) {
251        if (typeof callArray == 'undefined') {
252            callArray = calls;
253        }
254        print('callArray.length=' + callArray.length);
255        for (var i = 0; i < callArray.length; i++) {
256            if ((callArray[i] == null) || (typeof callArray[i] == 'undefined')) {
257                print('c[' + i + ']: undefined');
258            } else {
259                this.printCall(callArray[i]);
260            }
261        }
262    }
263
264    /**
265     * Count number of calls in the given state
266     *
267     * @param callState is the give state
268     */
269    this.countCallsInState = function(callState) {
270        var count = 0;
271        if ((callState < CALLSTATE_ACTIVE) || (callState > CALLSTATE_WAITING)) {
272            // not a valid call state
273            return null;
274        }
275        for (var i = 0; i < calls.length; i++) {
276            if (typeof calls[i] != 'undefined') {
277                if (calls[i].state == callState) {
278                    count++;
279                }
280            }
281        }
282        return count;
283    }
284
285    /**
286     * Print signal strength
287     */
288    this.printSignalStrength = function() {
289        print('rssi: '         + gwSignalStrength.signalStrength);
290        print('bitErrorRate: ' + gwSignalStrength.bitErrorRate);
291        print('cdmaDbm: '  +  cdmaSignalStrength.dbm);
292        print('cdmaEcio: ' + cdmaSignalStrength.ecio);
293        print('evdoRssi: ' + evdoSignalStrength.dbm);
294        print('evdoEcio: ' + evdoSignalStrength.ecio);
295        print('evdoSnr: '  + evdoSignalStrength.signalNoiseRatio);
296    }
297
298    /**
299     * set signal strength
300     *
301     * @param rssi and bitErrorRate are signal strength parameters for GSM
302     *        cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, evdoSnr are parameters for CDMA & EVDO
303     */
304    this.setSignalStrength = function(rssi, bitErrorRate, cdmaDbm, cdmaEcio, evdoRssi,
305                                      evdoEcio, evdoSnr) {
306        print('setSignalStrength E');
307
308        if (rssi != 99) {
309            if ((rssi < 0) || (rssi > 31)) {
310                throw ('not a valid signal strength');
311            }
312        }
313        // update signal strength
314        gwSignalStrength.signalStrength = rssi;
315        gwSignalStrength.bitErrorRate = bitErrorRate;
316        cdmaSignalStrength.dbm = cdmaDbm;
317        cdmaSignalStrength.ecio = cdmaEcio;
318        evdoSignalStrength.dbm = evdoRssi;
319        evdoSignalStrength.ecio = evdoEcio;
320        evdoSignalStrength.signalNoiseRatio = evdoSnr;
321
322        // pack the signal strength into RspSignalStrength and send a unsolicited response
323        var rsp = new Object();
324
325        rsp.gwSignalstrength = gwSignalStrength;
326        rsp.cdmSignalstrength = cdmaSignalStrength;
327        rsp.evdoSignalstrength = evdoSignalStrength;
328
329        var response = rilSchema[packageNameAndSeperator +
330                             'RspSignalStrength'].serialize(rsp);
331
332        sendRilUnsolicitedResponse(RIL_UNSOL_SIGNAL_STRENGTH, response);
333
334        // send the unsolicited signal strength every 1 minute.
335        simulatedRadioWorker.addDelayed(
336            {'reqNum' : CMD_UNSOL_SIGNAL_STRENGTH}, 60000);
337        print('setSignalStrength X');
338    }
339
340    /**
341     * Handle RIL_REQUEST_GET_CURRENT_CALL
342     *
343     * @param req is the Request
344     */
345    this.rilRequestGetCurrentCalls = function(req) { // 9
346        print('Radio: rilRequestGetCurrentCalls E');
347        var rsp = new Object();
348
349        // pack calls into rsp.calls
350        rsp.calls = new Array();
351        var i;
352        var j;
353        for (i = 0, j = 0; i < calls.length; i++) {
354            if (typeof calls[i] != 'undefined') {
355                rsp.calls[j++] = calls[i];
356            }
357        }
358        result.responseProtobuf = rilSchema[packageNameAndSeperator +
359                                            'RspGetCurrentCalls'].serialize(rsp);
360        return result;
361    }
362
363    /**
364     * Handle RIL_REQUEST_DIAL
365     *
366     * @param req is the Request
367     */
368    this.rilRequestDial = function(req) { // 10
369        print('Radio: rilRequestDial E');
370        var newCall = new Object();
371        newCall = this.addCall(CALLSTATE_DIALING, req.data.address, '');
372        if (newCall == null) {
373            result.rilErrCode = RIL_E_GENERIC_FAILURE;
374            return result;
375        }
376        this.printCalls(calls);
377
378        print('after add the call');
379        // Set call state to dialing
380        simulatedRadioWorker.add(
381                        {'reqNum' : CMD_CALL_STATE_CHANGE,
382                         'callType' : OUTGOING,
383                         'callIndex' : newCall.index,
384                         'nextState' : CALLSTATE_DIALING});
385        if (!callTransitionFlag) {
386            // for auto transition
387            // Update call state to alerting after 1 second
388            simulatedRadioWorker.addDelayed(
389                {'reqNum' : CMD_CALL_STATE_CHANGE,
390                 'callType' : OUTGOING,
391                 'callIndex' : newCall.index,
392                 'nextState' : CALLSTATE_ALERTING}, 1000);
393            // Update call state to active after 2 seconds
394            simulatedRadioWorker.addDelayed(
395                {'reqNum' : CMD_CALL_STATE_CHANGE,
396                 'callType' : OUTGOING,
397                 'callIndex': newCall.index,
398                 'nextState' : CALLSTATE_ACTIVE}, 2000);
399        }
400        return result;
401    }
402
403    /**
404     * Handle RIL_REQUEST_HANG_UP
405     *
406     * @param req is the Request
407     */
408    this.rilRequestHangUp = function(req) { // 12
409        print('Radio: rilRequestHangUp data.connection_index=' + req.data.connectionIndex);
410        if (this.removeCall(req.data.connectionIndex) == null) {
411            result.rilErrCode = RIL_E_GENERIC_FAILURE;
412            print('no connection to hangup');
413        }
414        return result;
415    }
416
417    /**
418     * Handle RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND
419     *        Hang up waiting or held
420     *
421     * @param req is the Request
422     */
423    this.rilRequestHangupWaitingOrBackground = function(req) { // 13
424        print('Radio: rilRequestHangupWaitingOrBackground');
425        if (numberActiveCalls <= 0) {
426          result.rilErrCode = RIL_E_GENERIC_FAILURE;
427        } else {
428            for (var i = 0; i < calls.length; i++) {
429                if (typeof calls[i] != 'undefined') {
430                    switch (calls[i].state) {
431                        case CALLSTATE_HOLDING:
432                        case CALLSTATE_WAITING:
433                        case CALLSTATE_INCOMING:
434                            this.removeCall(i);
435                            incomingCallIsProcessed = true;
436                            break;
437                        default:
438                            result.rilErrCode = RIL_E_GENERIC_FAILURE;
439                            break;
440                    }
441                    this.printCalls(calls);
442                    if(result.rilErrCode == RIL_E_GENERIC_FAILURE) {
443                        return result;
444                    }
445                } // end of processing call[i]
446            } // end of for
447        }
448        // Send out RIL_UNSOL_CALL_STATE_CHANGED after the request is returned
449        simulatedRadioWorker.add(
450          {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED});
451        return result;
452    }
453
454    /**
455     * Handle RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND
456     *   release all active calls (if any exist) and resume held or waiting calls.
457     * @param req is the Request
458     */
459    this.rilRequestHangUpForegroundResumeBackground = function(req) { //14
460        print('Radio: rilRequestHangUpForegroundResumeBackground');
461        if (numberActiveCalls <= 0) {
462            result.rilErrCode = RIL_E_GENERIC_FAILURE;
463        } else {
464            for (var i = 0; i < calls.length; i++) {
465                if (typeof calls[i] != 'undefined') {
466                    switch (calls[i].state) {
467                        case CALLSTATE_ACTIVE:
468                            this.removeCall(i);
469                            break;
470                        case CALLSTATE_HOLDING:
471                        case CALLSTATE_WAITING:
472                          calls[i].state = CALLSTATE_ACTIVE;
473                            break;
474                        default:
475                            result.rilErrCode = RIL_E_GENERIC_FAILURE;
476                            break;
477                    }
478                    this.printCalls(calls);
479                    if(result.rilErrCode == RIL_E_GENERIC_FAILURE) {
480                        return result;
481                    }
482                } // end of processing call[i]
483            }
484        }
485        // Send out RIL_UNSOL_CALL_STATE_CHANGED after the request is returned
486        simulatedRadioWorker.add(
487          {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED});
488        return result;
489    }
490
491    /**
492     * Handle RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE
493     *
494     *     BEFORE                               AFTER
495     * Call 1   Call 2                 Call 1       Call 2
496     * ACTIVE   HOLDING                HOLDING     ACTIVE
497     * ACTIVE   WAITING                HOLDING     ACTIVE
498     * HOLDING  WAITING                HOLDING     ACTIVE
499     * ACTIVE   IDLE                   HOLDING     IDLE
500     * IDLE     IDLE                   IDLE        IDLE
501     *
502     * @param req is the Request
503     */
504    this.rilRequestSwitchWaitingOrHoldingAndActive = function(req) {  // 15
505        print('Radio: rilRequestSwitchWaitingOrHoldingAndActive');
506        print('Radio: lastReq = ' + lastReq);
507        print('Radio: req.reqNum = ' + req.reqNum);
508        if (lastReq == req.reqNum) {
509            print('Radio: called twice');
510            return result;
511        }
512
513        if (numberActiveCalls <= 0) {
514            result.rilErrCode = RIL_E_GENERIC_FAILURE;
515        } else {
516            for (var i = 0; i < calls.length; i++) {
517                if (typeof calls[i] != 'undefined') {
518                    switch (calls[i].state) {
519                        case CALLSTATE_ACTIVE:
520                            calls[i].state = CALLSTATE_HOLDING;
521                            break;
522                        case CALLSTATE_HOLDING:
523                        case CALLSTATE_WAITING:
524                            calls[i].state = CALLSTATE_ACTIVE;
525                            break;
526                        default:
527                            result.rilErrCode = RIL_E_GENERIC_FAILURE;
528                            break;
529                    }
530                    this.printCalls(calls);
531                    if(result.rilErrCode == RIL_E_GENERIC_FAILURE) {
532                        return result;
533                    }
534                } // end of processing call[i]
535            } // end of for
536        }
537        // Send out RIL_UNSOL_CALL_STATE_CHANGED after the request is returned
538        simulatedRadioWorker.add(
539          {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED});
540        return result;
541    }
542
543    /**
544     * Handle RIL_REQUEST_CONFERENCE
545     * Conference holding and active
546     *
547     * @param req is the Request
548     */
549    this.rilRequestConference = function(req) {  // 16
550        print('Radio: rilRequestConference E');
551        if ((numberActiveCalls <= 0) || (numberActiveCalls > maxConnectionsPerCall)) {
552            // The maximum number of connections within a call is 5
553            result.rilErrCode = RIL_E_GENERIC_FAILURE;
554            return result;
555        } else {
556            var numCallsInBadState = 0;
557            for (var i = 0; i < calls.length; i++) {
558                if (typeof calls[i] != 'undefined') {
559                    if ((calls[i].state != CALLSTATE_ACTIVE) &&
560                            (calls[i].state != CALLSTATE_HOLDING)) {
561                        numCallsInBadState++;
562                    }
563                }
564            }
565
566            // if there are calls not in ACITVE or HOLDING state, return error
567            if (numCallsInBadState > 0) {
568                result.rilErrCode = RIL_E_GENERIC_FAILURE;
569                return result;
570            } else { // conference ACTIVE and HOLDING calls
571                for (var i = 0; i < calls.length; i++) {
572                    if (typeof calls[i] != 'undefined') {
573                        switch (calls[i].state) {
574                            case CALLSTATE_ACTIVE:
575                                break;
576                            case CALLSTATE_HOLDING:
577                                calls[i].state = CALLSTATE_ACTIVE;
578                                break;
579                            default:
580                                result.rilErrCode = RIL_E_GENERIC_FAILURE;
581                                break;
582                        }
583                    }
584                    this.printCalls(calls);
585                    if(result.rilErrCode == RIL_E_GENERIC_FAILURE) {
586                        return result;
587                    }
588                }
589            }
590        }
591
592        // Only if conferencing is successful,
593        // Send out RIL_UNSOL_CALL_STATE_CHANGED after the request is returned
594        simulatedRadioWorker.add(
595          {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED});
596        return result;
597    }
598
599    /**
600     * Handle RIL_REQUEST_LAST_CALL_FAIL_CAUSE
601     *
602     * @param req is the request
603     */
604    this.rilRequestLastCallFailCause = function(req) {
605        print('Radio: rilRequestLastCallFailCause E');
606
607        var rsp = new Object();
608        rsp.integers = new Array();
609        rsp.integers[0] = lastCallFailCause;
610
611        result.responseProtobuf = rilSchema[packageNameAndSeperator +
612                                            'RspIntegers'].serialize(rsp);
613        return result;
614    }
615
616    /**
617     * Handle RIL_REQUEST_SIGNAL_STRENGTH
618     *
619     * @param req is the Request
620     */
621    this.rilRequestSignalStrength = function(req) { // 19
622        print('Radio: rilRequestSignalStrength E');
623        var rsp = new Object();
624
625        // pack the signal strength into RspSignalStrength
626        rsp.gwSignalstrength = gwSignalStrength;
627        rsp.cdmaSignalstrength = cdmaSignalStrength;
628        rsp.evdoSignalstrength = evdoSignalStrength;
629
630        result.responseProtobuf = rilSchema[packageNameAndSeperator +
631                                            'RspSignalStrength'].serialize(rsp);
632        return result;
633    }
634
635    /**
636     * Handle RIL_REQUEST_REGISTRATION_STATE
637     *
638     * @param req is the Request
639     */
640    this.rilRequestRegistrationState = function(req) { // 20
641        print('Radio: rilRequestRegistrationState');
642
643        var rsp = new Object();
644        rsp.strings = Array();
645        rsp.strings[0] = registrationState;
646        rsp.strings[1] = lac;
647        rsp.strings[2] = cid;
648        rsp.strings[3] = radioTechnology;
649        rsp.strings[4] = baseStationId;
650        rsp.strings[5] = baseStationLatitude;
651        rsp.strings[6] = baseStationLongitude;
652        rsp.strings[7] = concurrentServices;
653        rsp.strings[8] = systemId;
654        rsp.strings[9] = networkId;
655        rsp.strings[10] = roamingIndicator;
656        rsp.strings[11] = prlActive;
657        rsp.strings[12] = defaultRoamingIndicator;
658        rsp.strings[13] = registrationDeniedReason;
659        rsp.strings[14] = primaryScrambingCode;
660
661        result.responseProtobuf = rilSchema[packageNameAndSeperator +
662                                 'RspStrings'].serialize(rsp);
663        return result;
664    }
665
666    /**
667     * Handle RIL_REQUEST_GPRS_REGISTRATION_STATE
668     *
669     * @param req is the Request
670     */
671    this.rilRequestGprsRegistrationState = function(req) { // 21
672        print('Radio: rilRequestGprsRegistrationState');
673
674        var rsp = new Object();
675        rsp.strings = Array();
676        rsp.strings[0] = registrationState;
677        rsp.strings[1] = lac;
678        rsp.strings[2] = cid;
679        rsp.strings[3] = radioTechnology;
680
681        result.responseProtobuf = rilSchema[packageNameAndSeperator +
682                                 'RspStrings'].serialize(rsp);
683        return result;
684    }
685
686    /**
687     * Handle RIL_REQUEST_ANSWER
688     *
689     * @param req is the Request
690     */
691    this.rilRequestAnswer = function(req) { // 40
692        print('Radio: rilRequestAnswer');
693
694        if (numberActiveCalls != 1) {
695            result.rilErrCode = RIL_E_GENERIC_FAILURE;
696            return result;
697        } else {
698            for (var i = 0; i < calls.length; i++) {
699                if (typeof calls[i] != 'undefined') {
700                    if (calls[i].state == CALLSTATE_INCOMING) {
701                        calls[i].state = CALLSTATE_ACTIVE;
702                        break;
703                    } else {
704                        result.rilErrCode = RIL_E_GENERIC_FAILURE;
705                        this.removeCall(i);
706                        return result;
707                    }
708                } // end of processing call[i]
709            } // end of for
710        }
711        incomingCallIsProcessed = true;
712        return result;
713    }
714
715    /**
716     * Handle RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE
717     *
718     * @param req is the Request
719     */
720    this.rilRequestQueryNeworkSelectionMode = function(req) { // 45
721        print('Radio: rilRequestQueryNeworkSelectionMode');
722
723        var rsp = new Object();
724        rsp.integers = Array();
725        rsp.integers[0] = networkSelectionMode;
726
727        result.responseProtobuf = rilSchema[packageNameAndSeperator +
728                                 'RspIntegers'].serialize(rsp);
729        return result;
730    }
731
732    /**
733     * Handle RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC
734     *
735     * @param req is the Request
736     */
737    this.rilRequestSetNeworkSelectionAutomatic = function(req) { // 46
738        print('Radio: rilRequestSetNeworkSelectionAutomatic');
739
740        networkSelectionMode = NETWORK_SELECTION_MODE_AUTOMATIC;
741
742        return result;
743    }
744
745    /**
746     * Handle RIL_REQUEST_BASE_BAND_VERSION
747     *
748     * @param req is the Request
749     */
750    this.rilRequestBaseBandVersion = function(req) { // 51
751        print('Radio: rilRequestBaseBandVersion');
752        var rsp = new Object();
753        rsp.strings = Array();
754        rsp.strings[0] = gBaseBandVersion;
755
756        result.responseProtobuf = rilSchema[packageNameAndSeperator +
757                                 'RspStrings'].serialize(rsp);
758        return result;
759    }
760
761    /**
762     * Handle RIL_REQUEST_SEPRATE_CONNECTION
763     * Separate a party from a multiparty call placing the multiparty call
764     * (less the specified party) on hold and leaving the specified party
765     * as the only other member of the current (active) call
766     *
767     * See TS 22.084 1.3.8.2 (iii)
768     *
769     * @param req is the Request
770     */
771    this.rilReqestSeparateConnection = function(req) {  // 52
772        print('Radio: rilReqestSeparateConnection');
773        var index = req.data.index;
774
775        if (numberActiveCalls <= 0) {
776            result.rilErrCode = RIL_E_GENERIC_FAILURE;
777            return result;
778        } else {
779            // get the connection to separate
780            var separateConn = this.getCall(req.data.index);
781            if (separateConn == null) {
782                result.rilErrCode = RIL_E_GENERIC_FAILURE;
783                return result;
784            } else {
785                if (separateConn.state != CALLSTATE_ACTIVE) {
786                    result.rilErrCode = RIL_E_GENERIC_FAILURE;
787                    return result;
788                }
789                // Put all other connections in hold.
790                for (var i = 0; i < calls.length; i++) {
791                    if (index != i) {
792                        // put all the active call to hold
793                        if (typeof calls[i] != 'undefined') {
794                            switch (calls[i].state) {
795                                case CALLSTATE_ACTIVE:
796                                    calls[i].state = CALLSTATE_HOLDING;
797                                    break;
798                                default:
799                                    result.rilErrCode = RIL_E_GENERIC_FAILURE;
800                                    break;
801                            }
802                            this.printCalls(calls);
803                            if(result.rilErrCode == RIL_E_GENERIC_FAILURE) {
804                                return result;
805                            }
806                        }  // end of processing call[i]
807                    }
808                }  // end of for
809            }
810        }
811
812        // Send out RIL_UNSOL_CALL_STATE_CHANGED after the request is returned
813        simulatedRadioWorker.add(
814          {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED});
815        return result;
816    }
817
818    /**
819     * Handle RIL_REQUEST_SET_MUTE
820     */
821    this.rilRequestSetMute = function(req) { // 53
822        print('Radio: rilRequestSetMute: req.data.state=' + req.data.state);
823        muteState = req.data.state;
824        if (gRadioState == RADIOSTATE_UNAVAILABLE) {
825            result.rilErrCode = RIL_E_RADIO_NOT_AVAILABLE;
826        }
827        return result;
828    }
829
830    /**
831     * Handle RIL_REQUEST_SCREEN_STATE
832     *
833     * @param req is the Request
834     */
835    this.rilRequestScreenState = function(req) { // 61
836        print('Radio: rilRequestScreenState: req.data.state=' + req.data.state);
837        screenState = req.data.state;
838        return result;
839    }
840
841    /**
842     * Delay test
843     */
844     this.cmdDelayTest = function(req) { // 2000
845         print('cmdDelayTest: req.hello=' + req.hello);
846         result.sendResponse = false;
847         return result;
848     }
849
850     /**
851      * Delay for RIL_UNSOL_SIGNAL_STRENGTH
852      * TODO: Simulate signal strength changes:
853      * Method 1: provide an array for signal strength, and send the unsolicited
854      *           reponse periodically (the period can also be simulated)
855      * Method 2: Simulate signal strength randomly (within a certain range) and
856      *           send the response periodically.
857      */
858     this.cmdUnsolSignalStrength = function(req) { // 2001
859         print('cmdUnsolSignalStrength: req.reqNum=' + req.reqNum);
860         var rsp = new Object();
861
862         // pack the signal strength into RspSignalStrength
863         rsp.gwSignalstrength = gwSignalStrength;
864         rsp.cdmaSignalstrength = cdmaSignalStrength;
865         rsp.evdoSignalstrength = evdoSignalStrength;
866
867         response = rilSchema[packageNameAndSeperator +
868                              'RspSignalStrength'].serialize(rsp);
869
870         // upldate signal strength
871         sendRilUnsolicitedResponse(RIL_UNSOL_SIGNAL_STRENGTH, response);
872
873         // Send the unsolicited signal strength every 1 minute.
874         simulatedRadioWorker.addDelayed(
875             {'reqNum' : CMD_UNSOL_SIGNAL_STRENGTH}, 60000);
876
877         // this is not a request, no response is needed
878         result.sendResponse = false;
879         return result;
880     }
881
882     /**
883      * Send RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED
884      */
885     this.cmdUnsolCallStateChanged = function(req) { // 2002
886         print('cmdUnsolCallStateChanged: req.reqNum=' + req.reqNum);
887         sendRilUnsolicitedResponse(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
888         result.sendResponse = false;
889         return result;
890     }
891
892     /**
893      * Simulate call state change
894      */
895     this.cmdCallStateChange = function(req) { // 2003
896         print('cmdCallStateChange: req.reqNum=' + req.reqNum);
897         print('cmdCallStateChange: req.callType=' + req.callType);
898         print('cmdCallStateChange: req.callIndex=' + req.callIndex);
899         print('cmdCallStateChange: req.nextState=' + req.nextState);
900
901         // if it is an outgoing call, update the call state of the call
902         // Send out call state changed flag
903         var curCall = this.getCall(req.callIndex);
904         this.printCall(curCall);
905
906         if (curCall != null) {
907             curCall.state = req.nextState;
908         } else {
909             this.removeCall(req.callIndex);
910         }
911
912         // TODO: if it is an incoming call, update the call state of the call
913         // Send out call state change flag
914         // Send out RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED
915         simulatedRadioWorker.add(
916           {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED});
917         result.sendResponse = false;
918         return result;
919     }
920
921     /**
922      * send UNSOL_CALL_STATE_CHANGED and UNSOL_CALL_RING
923      */
924     this.cmdUnsolCallRing = function(req) { // 2004
925         print('cmdUnsolCallRing: req.reqNum=' + req.reqNum);
926         if(!incomingCallIsProcessed) {
927             sendRilUnsolicitedResponse(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
928             sendRilUnsolicitedResponse(RIL_UNSOL_CALL_RING);
929
930             // Send the next alert in 3 seconds. [refer to ril.h definition]
931             simulatedRadioWorker.addDelayed(
932                 {'reqNum' : CMD_UNSOL_CALL_RING}, 3000);
933         }
934         result.sendResponse = false;
935         return result;
936     }
937
938     /**
939      * Create an incoming call for the giving number
940      * return CTRL_STATUS_ERR if there is already a call in any of the states of
941      * dialing, alerting, incoming, waiting [TS 22 030 6.5] , else
942      * return CTRL_STATUS_OK and update the call state
943      */
944     this.ctrlServerCmdStartInComingCall = function(req) {  // 1001
945         print('ctrlServerCmdStartInComingCall: req.reqNum:' + req.reqNum);
946         print('ctrlServerCmdStartInComingCall: req.data.phonenumber:' + req.data.phoneNumber);
947
948         var phoneNumber = req.data.phoneNumber;
949         var state;
950
951         if (numberActiveCalls <= 0) {
952             // If there is no connection in use, the call state is INCOMING
953             state = CALLSTATE_INCOMING;
954         } else {
955             // If there is call in any of the states of dialing, alerting, incoming
956             // waiting, this MT call can not be set
957             for (var i  = 0; i < calls.length; i++) {
958                 if (typeof calls[i] != 'undefined') {
959                     if ( (calls[i].state == CALLSTATE_DIALING) ||
960                          (calls[i].state == CALLSTATE_ALERTING) ||
961                          (calls[i].state == CALLSTATE_INCOMING) ||
962                          (calls[i].state == CALLSTATE_WAITING))
963                     {
964                         result.rilErrCode = CTRL_STATUS_ERR;
965                         return result;
966                     }
967                 }
968             }
969             // If the incoming call is a second call, the state is WAITING
970             state = CALLSTATE_WAITING;
971         }
972
973         // Add call to the call array
974         this.addCall(state, phoneNumber, '');
975
976         // set the incomingCallIsProcessed flag to be false
977         incomingCallIsProcessed = false;
978
979         simulatedRadioWorker.add(
980           {'reqNum' : CMD_UNSOL_CALL_RING});
981
982         result.rilErrCode = CTRL_STATUS_OK;
983         return result;
984    }
985
986    /**
987     * Handle control command CTRL_CMD_HANGUP_CONN_REMOTE
988     *   hangup the connection for the given failure cause
989     *
990     *@param req is the control request
991     */
992    this.ctrlServerCmdHangupConnRemote = function(req) {    // 1002
993        print('ctrlServerCmdHangupConnRemote: req.data.connectionId=' + req.data.connectionId +
994              ' req.data.callFailCause' + req.data.callFailCause);
995
996        var connection = req.data.connectionId;
997        var failureCause = req.data.callFailCause;
998
999        this.printCalls(calls);
1000        var hangupCall = this.removeCall(connection);
1001        if (hangupCall == null) {
1002            print('ctrlServerCmdHangupConnRemote: connection id is required.');
1003            result.rilErrCode = CTRL_STATUS_ERR;
1004            return result;
1005        } else {
1006            // for incoming call, stop sending call ring
1007            if ((hangupCall.state == CALLSTATE_INCOMING) ||
1008                (hangupCall.state == CALLSTATE_WAITING)) {
1009                incomingCallIsProcessed = true;
1010            }
1011        }
1012        this.printCalls(calls);
1013        lastCallFailCause = failureCause;
1014
1015        // send out call state changed response
1016        simulatedRadioWorker.add(
1017            {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED});
1018
1019        result.rilErrCode = CTRL_STATUS_OK;
1020        return result;
1021    }
1022
1023    /**
1024     * Set call transition flag
1025     */
1026    this.ctrlServerCmdSetCallTransitionFlag = function(req) {  // 1003
1027        print('ctrlServerCmdSetCallTransitionFlag: flag=' + req.data.flag);
1028
1029        callTransitionFlag = req.data.flag;
1030        result.rilErrCode = CTRL_STATUS_OK;
1031        return result;
1032    }
1033
1034    /**
1035     * Set the dialing call to alert
1036     */
1037    this.ctrlServerCmdSetCallAlert = function(req) {    // 1004
1038        print('ctrlServerCmdSetCallAlert: E');
1039
1040        if (callTransitionFlag == false) {
1041            print('ctrlServerCmdSetCallAlert: need to set the flag first');
1042            result.rilErrCode = CTRL_STATUS_ERR;
1043            return result;
1044        }
1045        if (numberActiveCalls <= 0) {
1046            print('ctrlServerCmdSetCallAlert: no active calls');
1047            result.rilErrCode = CTRL_STATUS_ERR;
1048            return result;
1049        }
1050        var dialingCalls = this.countCallsInState(CALLSTATE_DIALING);
1051        var index = this.getCallIdByState(CALLSTATE_DIALING);
1052        if ((dialingCalls == 1) && (index != null)) {
1053            calls[index].state = CALLSTATE_ALERTING;
1054        } else {
1055            // if there 0 or more than one call in dialing state, return error
1056            print('ctrlServerCmdSetCallAlert: no valid calls in dialing state');
1057            result.rilErrCode = CTRL_STATUS_ERR;
1058            return result;
1059        }
1060        // send unsolicited call state change response
1061        simulatedRadioWorker.add(
1062            {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED});
1063
1064        result.rilErrCode = CTRL_STATUS_OK;
1065        return result;
1066    }
1067
1068    /**
1069     * Set the alserting call to active
1070     */
1071    this.ctrlServerCmdSetCallActive = function(req) {   // 1005
1072        print('ctrlServerCmdSetCallActive: E');
1073
1074        if (callTransitionFlag == false) {
1075            print('ctrlServerCmdSetCallActive: need to set the flag firt');
1076            result.rilErrCode = CTRL_STATUS_ERR;
1077            return result;
1078        }
1079        if (numberActiveCalls <= 0) {
1080            print('ctrlServerCmdSetCallActive: no active calls');
1081            result.rilErrCode = CTRL_STATUS_ERR;
1082            return result;
1083        }
1084        var alertingCalls = this.countCallsInState(CALLSTATE_ALERTING);
1085        var index = this.getCallIdByState(CALLSTATE_ALERTING);
1086        if ((alertingCalls == 1) && (index != null)) {
1087            calls[index].state = CALLSTATE_ACTIVE;
1088        } else {
1089            print('ctrlServerCmdSetCallActive: no valid calls in alert state');
1090            result.rilErrCode = CTRL_STATUS_ERR;
1091            return result;
1092        }
1093        // send out unsolicited call state change response
1094        simulatedRadioWorker.add(
1095            {'reqNum' : CMD_UNSOL_CALL_STATE_CHANGED});
1096
1097        result.rilErrCode = CTRL_STATUS_OK;
1098        return result;
1099    }
1100
1101    /**
1102     * Add a dialing call
1103     */
1104    this.ctrlServerCmdAddDialingCall = function(req) {   // 1006
1105        print('ctrlServerCmdAddDialingCall: E');
1106
1107        var phoneNumber = req.data.phoneNumber;
1108        var dialingCalls = this.countCallsInState(CALLSTATE_DIALING);
1109        if (dialingCalls == 0) {
1110            this.addCall(CALLSTATE_DIALING, phoneNumber, '');
1111            result.rilErrCode = CTRL_STATUS_OK;
1112        } else {
1113            print('ctrlServerCmdAddDialingCall: add dialing call failed');
1114            result.rilErrCode = CTRL_STATUS_ERR;
1115        }
1116        return result;
1117    }
1118
1119    /**
1120     * Process the request by dispatching to the request handlers
1121     */
1122    this.process = function(req) {
1123        try {
1124            print('Radio E: req.reqNum=' + req.reqNum + ' req.token=' + req.token);
1125
1126            // Assume the result will be true, successful and nothing to return
1127            result.sendResponse = true;
1128            result.rilErrCode = RIL_E_SUCCESS;
1129            result.responseProtobuf = emptyProtobuf;
1130
1131            try {
1132                // Pass "this" object to each ril request call such that
1133                // they have the same scope
1134                result = (this.radioDispatchTable[req.reqNum]).call(this, req);
1135            } catch (err) {
1136                print('Radio:process err = ' + err);
1137                print('Radio: Unknown reqNum=' + req.reqNum);
1138                result.rilErrCode = RIL_E_REQUEST_NOT_SUPPORTED;
1139            }
1140
1141            if (req.reqNum < 200) {
1142                lastReq = req.reqNum;
1143            }
1144            if (result.sendResponse) {
1145                if (isCtrlServerDispatchCommand(req.reqNum)) {
1146                    //print('Command ' + req.reqNum + ' is a control server command');
1147                    sendCtrlRequestComplete(result.rilErrCode, req.reqNum,
1148                      req.token, result.responseProtobuf);
1149                } else {
1150                    //print('Request ' + req.reqNum + ' is a ril request');
1151                    sendRilRequestComplete(result.rilErrCode, req.reqNum,
1152                      req.token, result.responseProtobuf);
1153                }
1154            }
1155            //print('Radio X: req.reqNum=' + req.reqNum + ' req.token=' + req.token);
1156        } catch (err) {
1157            print('Radio: Exception req.reqNum=' +
1158                    req.reqNum + ' req.token=' + req.token + ' err=' + err);
1159        }
1160    }
1161
1162    /**
1163     * Construct the simulated radio
1164     */
1165    print('Radio: constructor E');
1166    this.radioDispatchTable = new Array();
1167    this.radioDispatchTable[RIL_REQUEST_GET_CURRENT_CALLS] = // 9
1168                this.rilRequestGetCurrentCalls;
1169    this.radioDispatchTable[RIL_REQUEST_DIAL] = // 10
1170                this.rilRequestDial;
1171    this.radioDispatchTable[RIL_REQUEST_HANGUP] =  // 12
1172                this.rilRequestHangUp;
1173    this.radioDispatchTable[RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND] = // 13
1174                this.rilRequestHangupWaitingOrBackground;
1175    this.radioDispatchTable[RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND] =  // 14
1176                this.rilRequestHangUpForegroundResumeBackground;
1177    this.radioDispatchTable[RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] =  // 15
1178                this.rilRequestSwitchWaitingOrHoldingAndActive;
1179    this.radioDispatchTable[RIL_REQUEST_CONFERENCE] = // 16
1180                this.rilRequestConference;
1181    this.radioDispatchTable[RIL_REQUEST_LAST_CALL_FAIL_CAUSE] = // 18
1182                this.rilRequestLastCallFailCause;
1183    this.radioDispatchTable[RIL_REQUEST_SIGNAL_STRENGTH] = // 19
1184                this.rilRequestSignalStrength;
1185    this.radioDispatchTable[RIL_REQUEST_REGISTRATION_STATE] = // 20
1186                this.rilRequestRegistrationState;
1187    this.radioDispatchTable[RIL_REQUEST_GPRS_REGISTRATION_STATE] = // 21
1188                this.rilRequestGprsRegistrationState;
1189    this.radioDispatchTable[RIL_REQUEST_ANSWER] = // 40
1190                this.rilRequestAnswer;
1191    this.radioDispatchTable[RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE] = // 45
1192                this.rilRequestQueryNeworkSelectionMode;
1193    this.radioDispatchTable[RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC] = // 46
1194                this.rilRequestSetNeworkSelectionAutomatic;
1195    this.radioDispatchTable[RIL_REQUEST_BASEBAND_VERSION] = // 51
1196                this.rilRequestBaseBandVersion;
1197    this.radioDispatchTable[RIL_REQUEST_SEPARATE_CONNECTION] = // 52
1198                this.rilReqestSeparateConnection;
1199    this.radioDispatchTable[RIL_REQUEST_SET_MUTE] = // 53
1200                this.rilRequestSetMute;
1201    this.radioDispatchTable[RIL_REQUEST_SCREEN_STATE] = // 61
1202                this.rilRequestScreenState;
1203
1204    this.radioDispatchTable[CTRL_CMD_SET_MT_CALL] = //1001
1205                this.ctrlServerCmdStartInComingCall;
1206    this.radioDispatchTable[CTRL_CMD_HANGUP_CONN_REMOTE] = //1002
1207                this.ctrlServerCmdHangupConnRemote;
1208    this.radioDispatchTable[CTRL_CMD_SET_CALL_TRANSITION_FLAG] = //1003
1209                this.ctrlServerCmdSetCallTransitionFlag;
1210    this.radioDispatchTable[CTRL_CMD_SET_CALL_ALERT] =  // 1004
1211                this.ctrlServerCmdSetCallAlert;
1212    this.radioDispatchTable[CTRL_CMD_SET_CALL_ACTIVE] =  // 1005
1213                this.ctrlServerCmdSetCallActive;
1214    this.radioDispatchTable[CTRL_CMD_ADD_DIALING_CALL] =  // 1006
1215                this.ctrlServerCmdAddDialingCall;
1216
1217    this.radioDispatchTable[CMD_DELAY_TEST] = // 2000
1218                this.cmdDelayTest;
1219    this.radioDispatchTable[CMD_UNSOL_SIGNAL_STRENGTH] =  // 2001
1220                this.cmdUnsolSignalStrength;
1221    this.radioDispatchTable[CMD_UNSOL_CALL_STATE_CHANGED] =  // 2002
1222                this.cmdUnsolCallStateChanged;
1223    this.radioDispatchTable[CMD_CALL_STATE_CHANGE] = //2003
1224                this.cmdCallStateChange;
1225    this.radioDispatchTable[CMD_UNSOL_CALL_RING] = //2004
1226                this.cmdUnsolCallRing;
1227
1228    print('Radio: constructor X');
1229}
1230
1231// The simulated radio instance and its associated Worker
1232var simulatedRadio = new Radio();
1233var simulatedRadioWorker = new Worker(function (req) {
1234    simulatedRadio.process(req);
1235});
1236simulatedRadioWorker.run();
1237
1238// TODO: this is a workaround for bug http://b/issue?id=3001613
1239// When adding a new all, two RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE
1240// will be sent from the framework.
1241var lastReq = 0;
1242
1243/**
1244 * Optional tests
1245 */
1246if (false) {
1247    include("simulated_radio_tests.js");
1248}
1249