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