| /** |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @fileoverview Mock Radio Interface Layer (RIL) used for testing |
| * |
| * The following routines are defined in c++: |
| * |
| * Print a string to android log |
| * print(string) |
| * |
| * Read a file to a string. |
| * String readFileToString(String fileName) |
| * |
| * Read a file to a Buffer. |
| * Buffer readFileToBuffer(String fileName) |
| * |
| * Send an response unsolicited response to the framework. |
| * sendRilUnsolicitedResponse(Number responseNum, Buffer responseProtobuf) |
| * |
| * Send a completion request to the framework. |
| * sendRilRequestComplete(Number rilErrCode, Number reqNum, |
| * String token, Buffer responseProtobuf) |
| * |
| * Send a complete request to the controller. |
| * sendCtrlRequestComplete(Number ctrlStatus, Number reqNum, |
| * String token, Buffer responseProtobuf) |
| * |
| * Include the javascript file. |
| * include(string) |
| * |
| * The following objects are defined in c++ |
| * |
| * Buffer is defined in node_buffer and provides a wrapper |
| * for a buffer that can be shared between c++ and js. |
| * Buffer(length) |
| * Buffer::length() |
| * Buffer::data() |
| * |
| * Schema is defined in protobuf_v8 and converts between |
| * a buffer and an object. A protobuf descriptor, ril.desc |
| * and ctrl.desc, is used to drive the conversation. |
| * Object Schema::parse(Buffer protobuf) |
| * Buffer Schema::serialize(object) |
| * |
| * Worker is a thread which receives messages to be handled. |
| * It is passed a function which is called once for each |
| * message as it arrives. Call the add method to queue up |
| * requests for the worker function to process. |
| * Object Worker(function (req)) |
| * Worker::add(req); |
| */ |
| |
| /** |
| * Globals |
| */ |
| |
| include("ril_vars.js"); |
| |
| var NULL_RESPONSE_STRING = '*magic-null*'; |
| |
| // The state of the radio, needed by currentState() |
| var gRadioState = RADIOSTATE_UNAVAILABLE; |
| |
| // The state of the screen |
| var gScreenState = 0; |
| |
| // The base band version |
| var gBaseBandVersion = 'mock-ril 0.1'; |
| |
| // define a global variable to access the global object |
| var globals = this; |
| |
| // Empty Protobuf, defined here so we don't have |
| // to recreate an empty Buffer frequently |
| var emptyProtobuf = new Buffer(); |
| |
| // Get the ril description file and create a schema |
| var packageNameAndSeperator = 'ril_proto.'; |
| var rilSchema = new Schema(readFileToBuffer('ril.desc')); |
| var ctrlSchema = new Schema(readFileToBuffer('ctrl.desc')); |
| |
| /** |
| * Print properties of an object |
| */ |
| function printProperties(obj, maxDepth, depth) { |
| if (typeof maxDepth == 'undefined') { |
| maxDepth = 1; |
| } |
| if (typeof depth == 'undefined') { |
| depth = 1; |
| } |
| if (depth == 1) { |
| print('printProperties:'); |
| } |
| for (var property in obj) { |
| try { |
| if ((typeof obj[property] == 'object') |
| && (depth < maxDepth)) { |
| printProperties(obj[property], maxDepth, depth+1); |
| } else { |
| print(depth + ': ' + property + '=' + obj[property] + |
| ' type=' + typeof obj[property]); |
| } |
| } catch (err) { |
| print('err=' + err) |
| } |
| } |
| } |
| |
| // Test printProperties |
| if (false) { |
| var myObject = { 'field1' : '1', 'field2' : '2', 'hello' : [ 'hi', 'there' ] }; |
| printProperties(myObject, 3); |
| } |
| |
| /** |
| * Include the components |
| */ |
| |
| include("simulated_radio.js"); |
| include("simulated_icc.js"); |
| include("ctrl_server.js"); |
| |
| /** |
| * Construct a new request which is passed to the |
| * Worker handler method. |
| */ |
| function Request(reqNum, token, protobuf, schema, schemaName) { |
| this.reqNum = reqNum; |
| this.token = token; |
| try { |
| this.data = schema[packageNameAndSeperator + schemaName].parse(protobuf); |
| } catch (err) { |
| // not a valid protobuf in the request |
| this.data = null; |
| } |
| } |
| |
| /** |
| * Dispatch incoming requests from RIL to the appropriate component. |
| */ |
| function onRilRequest(reqNum, token, requestProtobuf) { |
| try { |
| //print('onRilRequest E: reqNum=' + reqNum + ' token=' + token); |
| |
| /** |
| * Validate parameters |
| */ |
| rilErrCode = RIL_E_SUCCESS; |
| if (typeof reqNum != 'number') { |
| print('onRilRequest: reqNum is not a number'); |
| rilErrCode = RIL_E_GENERIC_FAILURE; |
| } |
| if (typeof token != 'number') { |
| print('onRilRequest: token is not a number'); |
| rilErrCode = RIL_E_GENERIC_FAILURE; |
| } |
| if (typeof requestProtobuf != 'object') { |
| print('onRilRequest: requestProtobuf is not an object'); |
| rilErrCode = RIL_E_GENERIC_FAILURE; |
| } |
| if (rilErrCode != RIL_E_SUCCESS) { |
| sendRilRequestComplete(rilErrCode, reqNum, token); |
| return 'onRilRequest X: invalid parameter'; |
| } |
| |
| try { |
| //print('onRilRequest: get entry from dispatchTable reqNum=' + reqNum); |
| entry = dispatchTable[reqNum]; |
| if (typeof entry == 'undefined') { |
| throw ('entry = dispatchTable[' + reqNum + '] was undefined'); |
| } else { |
| req = new Request(reqNum, token, requestProtobuf, rilSchema, entry.schemaName); |
| for(i = 0; i < entry.components.length; i++) { |
| entry.components[i].add(req); |
| } |
| } |
| } catch (err) { |
| print('onRilRequest: Unknown reqNum=' + reqNum + ' err=' + err); |
| sendRilRequestComplete(RIL_E_REQUEST_NOT_SUPPORTED, reqNum, token); |
| } |
| // print('onRilRequest X: reqNum=' + reqNum + ' token=' + token); |
| } catch (err) { |
| print('onRilRequest X: Exception err=' + err); |
| return('onRilRequest X: Exception err=' + err); |
| } |
| return 'onRilRequest X'; |
| } |
| |
| function onUnsolicitedTick(tick) { |
| print('onUnsolicitedTick EX tick=' + tick); |
| return 3; |
| } |
| |
| /** |
| * Dispatch table for requests |
| * |
| * Each table entry is index by the RIL_REQUEST_xxxx |
| * and contains an array of components this request |
| * is to be sent to and the name of the schema |
| * that converts the incoming protobuf to the |
| * appropriate request data. |
| * |
| * DispatchTable[RIL_REQUEST_xxx].components = Array of components |
| * DisptachTable[RIL_REQUEST_xxx].Entry.schemaName = 'Name-of-schema'; |
| */ |
| var dispatchTable = new Array(); |
| |
| dispatchTable[RIL_REQUEST_GET_SIM_STATUS] = { // 1 |
| 'components' : [simulatedIccWorker], |
| 'schemaName' : 'ReqGetSimStatus', |
| }; |
| dispatchTable[RIL_REQUEST_ENTER_SIM_PIN] = { // 2 |
| 'components' : [simulatedIccWorker], |
| 'schemaName' : 'ReqEnterSimPin', |
| }; |
| dispatchTable[RIL_REQUEST_GET_CURRENT_CALLS] = { // 9 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_DIAL] = { // 10 |
| 'components' : [simulatedRadioWorker], |
| 'schemaName' : 'ReqDial', |
| }; |
| dispatchTable[RIL_REQUEST_GET_IMSI] = { // 11 |
| 'components' : [simulatedIccWorker], |
| }; |
| dispatchTable[RIL_REQUEST_HANGUP] = { // 12 |
| 'components' : [simulatedRadioWorker], |
| 'schemaName' : 'ReqHangUp', |
| }; |
| dispatchTable[RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND] = { // 13 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND] = { // 14 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] = { // 15 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_CONFERENCE] = { // 16 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_LAST_CALL_FAIL_CAUSE] = { // 18 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_SIGNAL_STRENGTH] = { // 19 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_VOICE_REGISTRATION_STATE] = { // 20 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_DATA_REGISTRATION_STATE] = { // 21 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_OPERATOR] = { // 22 |
| 'components' : [simulatedIccWorker], |
| }; |
| dispatchTable[RIL_REQUEST_GET_IMEI] = { // 38 |
| 'components' : [simulatedIccWorker], |
| }; |
| dispatchTable[RIL_REQUEST_GET_IMEISV] = { // 39 |
| 'components' : [simulatedIccWorker], |
| }; |
| dispatchTable[RIL_REQUEST_ANSWER] = { // 40 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE] = { // 45 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC] = { // 46 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_BASEBAND_VERSION] = { // 51 |
| 'components' : [simulatedRadioWorker], |
| }; |
| dispatchTable[RIL_REQUEST_SEPARATE_CONNECTION] = { // 52 |
| 'components' : [simulatedRadioWorker], |
| 'schemaName' : 'ReqSeparateConnection', |
| }; |
| dispatchTable[RIL_REQUEST_SET_MUTE ] = { // 53 |
| 'components' : [simulatedRadioWorker], |
| 'schemaName' : 'ReqSetMute', |
| }; |
| dispatchTable[RIL_REQUEST_SCREEN_STATE] = { // 61 |
| 'components' : [simulatedRadioWorker], |
| 'schemaName' : 'ReqScreenState', |
| }; |
| |
| /** |
| * Start the mock rill after loading |
| */ |
| function startMockRil() { |
| print("startMockRil E:"); |
| setRadioState(RADIOSTATE_SIM_READY); |
| // send the signal strength after 5 seconds, wait until mock ril is started |
| simulatedRadioWorker.addDelayed({ |
| 'reqNum' : CMD_UNSOL_SIGNAL_STRENGTH}, 5000); |
| print("startMockRil X:"); |
| } |
| |
| /** |
| * Optional tests |
| */ |
| if (false) { |
| include("mock_ril_tests.js"); |
| } |