| /** |
| * 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. |
| */ |
| |
| #include <map> |
| |
| #include <v8.h> |
| #include "ril.h" |
| |
| |
| #include "hardware/ril/mock-ril/src/proto/ril.pb.h" |
| |
| #include "logging.h" |
| #include "js_support.h" |
| #include "mock_ril.h" |
| #include "node_buffer.h" |
| #include "node_object_wrap.h" |
| #include "node_util.h" |
| #include "protobuf_v8.h" |
| #include "status.h" |
| #include "util.h" |
| #include "worker.h" |
| |
| #include "requests.h" |
| |
| //#define REQUESTS_DEBUG |
| #ifdef REQUESTS_DEBUG |
| |
| #define DBG(...) ALOGD(__VA_ARGS__) |
| |
| #else |
| |
| #define DBG(...) |
| |
| #endif |
| |
| |
| /** |
| * Request has no data so create an empty Buffer |
| */ |
| int ReqWithNoData(Buffer **pBuffer, |
| const void *data, const size_t datalen, const RIL_Token t) { |
| int status; |
| static Buffer *emptyBuffer = Buffer::New(0L); |
| |
| DBG("ReqWithNoData E"); |
| *pBuffer = emptyBuffer; |
| status = STATUS_OK; |
| |
| DBG("ReqWithNoData X status=%d", status); |
| return status; |
| } |
| |
| /** |
| * request for RIL_REQUEST_ENTER_SIM_PIN // 2 |
| */ |
| int ReqEnterSimPin(Buffer **pBuffer, |
| const void *data, const size_t datalen, const RIL_Token t) { |
| int status; |
| Buffer *buffer; |
| |
| DBG("ReqEnterSimPin E"); |
| if (datalen < sizeof(int)) { |
| ALOGE("ReqEnterSimPin: data to small err size < sizeof int"); |
| status = STATUS_BAD_DATA; |
| } else { |
| ril_proto::ReqEnterSimPin *req = new ril_proto::ReqEnterSimPin(); |
| DBG("ReqEnterSimPin: pin = %s", ((const char **)data)[0]); |
| req->set_pin((((char **)data)[0])); |
| buffer = Buffer::New(req->ByteSize()); |
| req->SerializeToArray(buffer->data(), buffer->length()); |
| delete req; |
| *pBuffer = buffer; |
| status = STATUS_OK; |
| } |
| DBG("ReqEnterSimPin X status=%d", status); |
| return status; |
| } |
| |
| /** |
| * request for RIL_REQUEST_DIAL // 10 |
| */ |
| int ReqDial(Buffer **pBuffer, |
| const void *data, const size_t datalen, const RIL_Token t) { |
| int status; |
| Buffer *buffer; |
| |
| DBG("ReqDial E"); |
| DBG("data=%p datalen=%d t=%p", data, datalen, t); |
| |
| if (datalen < sizeof(int)) { |
| ALOGE("ReqDial: data to small err size < sizeof int"); |
| status = STATUS_BAD_DATA; |
| } else { |
| ril_proto::ReqDial *req = new ril_proto::ReqDial(); |
| |
| // cast the data to RIL_Dial |
| RIL_Dial *rilDial = (RIL_Dial *)data; |
| DBG("ReqDial: rilDial->address =%s, rilDial->clir=%d", rilDial->address, rilDial->clir); |
| |
| req->set_address(rilDial->address); |
| req->set_clir(rilDial->clir); |
| ril_proto::RilUusInfo *uusInfo = (ril_proto::RilUusInfo *)(&(req->uus_info())); |
| |
| if (rilDial->uusInfo != NULL) { |
| DBG("ReqDial: print uusInfo:"); |
| DBG("rilDial->uusInfo->uusType = %d, " |
| "rilDial->uusInfo->uusDcs =%d, " |
| "rilDial->uusInfo->uusLength=%d, " |
| "rilDial->uusInfo->uusData = %s", |
| rilDial->uusInfo->uusType, |
| rilDial->uusInfo->uusDcs, |
| rilDial->uusInfo->uusLength, |
| rilDial->uusInfo->uusData); |
| |
| uusInfo->set_uus_type((ril_proto::RilUusType)rilDial->uusInfo->uusType); |
| uusInfo->set_uus_dcs((ril_proto::RilUusDcs)rilDial->uusInfo->uusDcs); |
| uusInfo->set_uus_length(rilDial->uusInfo->uusLength); |
| uusInfo->set_uus_data(rilDial->uusInfo->uusData); |
| } else { |
| DBG("uusInfo is NULL"); |
| } |
| |
| DBG("ReqDial: after set the request"); |
| DBG("req->ByetSize=%d", req->ByteSize()); |
| buffer = Buffer::New(req->ByteSize()); |
| DBG("buffer size=%d", buffer->length()); |
| |
| req->SerializeToArray(buffer->data(), buffer->length()); |
| delete req; |
| *pBuffer = buffer; |
| status = STATUS_OK; |
| DBG("ReqDial X, buffer->length()=%d", buffer->length()); |
| } |
| DBG("ReqDial X status = %d", status); |
| return status; |
| } |
| |
| /** |
| * request for RIL_REQUEST_HANGUP // 12 |
| */ |
| int ReqHangUp(Buffer **pBuffer, |
| const void *data, const size_t datalen, const RIL_Token t) { |
| int status; |
| Buffer *buffer; |
| |
| DBG("ReqHangUp E"); |
| if (datalen < sizeof(int)) { |
| ALOGE("ReqHangUp: data to small err size < sizeof int"); |
| status = STATUS_BAD_DATA; |
| } else { |
| ril_proto::ReqHangUp *req = new ril_proto::ReqHangUp(); |
| DBG("ReqHangUp: connection_index=%d", ((int *)data)[0]); |
| req->set_connection_index(((int *)data)[0]); |
| buffer = Buffer::New(req->ByteSize()); |
| req->SerializeToArray(buffer->data(), buffer->length()); |
| delete req; |
| *pBuffer = buffer; |
| status = STATUS_OK; |
| } |
| DBG("ReqHangUp X status=%d", status); |
| return status; |
| } |
| |
| /** |
| * request for RIL_REQUEST_SEPARATE_CONNECTION // 52 |
| */ |
| int ReqSeparateConnection (Buffer **pBuffer, |
| const void *data, const size_t datalen, const RIL_Token t) { |
| int status; |
| Buffer *buffer; |
| v8::HandleScope handle_scope; |
| |
| DBG("ReqSeparateConnection E"); |
| if (datalen < sizeof(int)) { |
| ALOGE("ReqSetMute: data to small err size < sizeof int"); |
| status = STATUS_BAD_DATA; |
| } else { |
| ril_proto::ReqSeparateConnection *req = new ril_proto::ReqSeparateConnection(); |
| DBG("ReqSeparateConnection: index=%d", ((int *)data)[0]); |
| req->set_index(((int *)data)[0]); |
| DBG("ReqSeparateConnection: req->ByetSize=%d", req->ByteSize()); |
| buffer = Buffer::New(req->ByteSize()); |
| req->SerializeToArray(buffer->data(), buffer->length()); |
| delete req; |
| *pBuffer = buffer; |
| status = STATUS_OK; |
| } |
| DBG("ReqSeparateConnection X status=%d", status); |
| return status; |
| } |
| |
| /** |
| * request for RIL_REQUEST_SET_MUTE // 53 |
| */ |
| int ReqSetMute(Buffer **pBuffer, |
| const void *data, const size_t datalen, const RIL_Token t) { |
| int status; |
| Buffer *buffer; |
| v8::HandleScope handle_scope; |
| |
| DBG("ReqSetMute E"); |
| if (datalen < sizeof(int)) { |
| ALOGE("ReqSetMute: data to small err size < sizeof int"); |
| status = STATUS_BAD_DATA; |
| } else { |
| ril_proto::ReqSetMute *req = new ril_proto::ReqSetMute(); |
| DBG("ReqSetMute: state=%d", ((int *)data)[0]); |
| req->set_state(((int *)data)[0]); |
| DBG("ReqSetMute: req->ByetSize=%d", req->ByteSize()); |
| buffer = Buffer::New(req->ByteSize()); |
| req->SerializeToArray(buffer->data(), buffer->length()); |
| delete req; |
| *pBuffer = buffer; |
| status = STATUS_OK; |
| } |
| DBG("ReqSetMute X status=%d", status); |
| return status; |
| } |
| |
| /** |
| * request for RIL_REQUEST_SCREEN_STATE // 61 |
| */ |
| int ReqScreenState(Buffer **pBuffer, |
| const void *data, const size_t datalen, const RIL_Token t) { |
| int status; |
| Buffer *buffer; |
| v8::HandleScope handle_scope; |
| |
| DBG("ReqScreenState E data=%p datalen=%d t=%p", |
| data, datalen, t); |
| if (datalen < sizeof(int)) { |
| ALOGE("ReqScreenState: data to small err size < sizeof int"); |
| status = STATUS_BAD_DATA; |
| } else { |
| ril_proto::ReqScreenState *req = new ril_proto::ReqScreenState(); |
| DBG("ReqScreenState: state=%d", ((int *)data)[0]); |
| req->set_state(((int *)data)[0]); |
| DBG("ReqScreenState: req->ByteSize()=%d", req->ByteSize()); |
| buffer = Buffer::New(req->ByteSize()); |
| DBG("ReqScreenState: serialize"); |
| req->SerializeToArray(buffer->data(), buffer->length()); |
| delete req; |
| *pBuffer = buffer; |
| status = STATUS_OK; |
| } |
| DBG("ReqScreenState X status=%d", status); |
| return status; |
| } |
| |
| /** |
| * Map from indexed by cmd and used to convert Data to Protobuf. |
| */ |
| typedef int (*ReqConversion)(Buffer** pBuffer, const void *data, |
| const size_t datalen, const RIL_Token t); |
| typedef std::map<int, ReqConversion> ReqConversionMap; |
| ReqConversionMap rilReqConversionMap; |
| |
| int callOnRilRequest(v8::Handle<v8::Context> context, int cmd, |
| const void *buffer, RIL_Token t) { |
| DBG("callOnRilRequest E: cmd=%d", cmd); |
| |
| int status; |
| v8::HandleScope handle_scope; |
| v8::TryCatch try_catch; |
| |
| // Get the onRilRequest Function |
| v8::Handle<v8::String> name = v8::String::New("onRilRequest"); |
| v8::Handle<v8::Value> onRilRequestFunctionValue = context->Global()->Get(name); |
| v8::Handle<v8::Function> onRilRequestFunction = |
| v8::Handle<v8::Function>::Cast(onRilRequestFunctionValue); |
| |
| // Create the cmd and token |
| v8::Handle<v8::Value> v8RequestValue = v8::Number::New(cmd); |
| v8::Handle<v8::Value> v8TokenValue = v8::Number::New(int64_t(t)); |
| |
| // Invoke onRilRequest |
| const int argc = 3; |
| v8::Handle<v8::Value> argv[argc] = { |
| v8RequestValue, v8TokenValue, ((Buffer *)buffer)->handle_ }; |
| v8::Handle<v8::Value> result = |
| onRilRequestFunction->Call(context->Global(), argc, argv); |
| if (try_catch.HasCaught()) { |
| ALOGE("callOnRilRequest error"); |
| ReportException(&try_catch); |
| status = STATUS_ERR; |
| } else { |
| v8::String::Utf8Value result_string(result); |
| DBG("callOnRilRequest result=%s", ToCString(result_string)); |
| status = STATUS_OK; |
| } |
| |
| DBG("callOnRilRequest X: status=%d", status); |
| return status; |
| } |
| |
| RilRequestWorkerQueue::RilRequestWorkerQueue(v8::Handle<v8::Context> context) { |
| DBG("RilRequestWorkerQueue E:"); |
| |
| context_ = context; |
| pthread_mutex_init(&free_list_mutex_, NULL); |
| |
| DBG("RilRequestWorkerQueue X:"); |
| } |
| |
| RilRequestWorkerQueue::~RilRequestWorkerQueue() { |
| DBG("~RilRequestWorkerQueue E:"); |
| Request *req; |
| pthread_mutex_lock(&free_list_mutex_); |
| while(free_list_.size() != 0) { |
| req = free_list_.front(); |
| delete req; |
| free_list_.pop(); |
| } |
| pthread_mutex_unlock(&free_list_mutex_); |
| pthread_mutex_destroy(&free_list_mutex_); |
| DBG("~RilRequestWorkerQueue X:"); |
| } |
| |
| /** |
| * Add a request to the processing queue. |
| * Data is serialized to a protobuf before adding to the queue. |
| */ |
| void RilRequestWorkerQueue::AddRequest (const int request, |
| const void *data, const size_t datalen, const RIL_Token token) { |
| DBG("RilRequestWorkerQueue:AddRequest: %d E", request); |
| |
| v8::Locker locker; |
| v8::HandleScope handle_scope; |
| v8::Context::Scope context_scope(context_); |
| |
| int status; |
| |
| // Convert the data to a protobuf before inserting it into the request queue (serialize data) |
| Buffer *buffer = NULL; |
| ReqConversionMap::iterator itr; |
| itr = rilReqConversionMap.find(request); |
| if (itr != rilReqConversionMap.end()) { |
| status = itr->second(&buffer, data, datalen, token); |
| } else { |
| ALOGE("RilRequestWorkerQueue:AddRequest: X unknown request %d", request); |
| status = STATUS_UNSUPPORTED_REQUEST; |
| } |
| |
| if (status == STATUS_OK) { |
| // Add serialized request to the queue |
| Request *req; |
| pthread_mutex_lock(&free_list_mutex_); |
| DBG("RilRequestWorkerQueue:AddRequest: return ok, buffer = %p, buffer->length()=%d", |
| buffer, buffer->length()); |
| if (free_list_.size() == 0) { |
| req = new Request(request, buffer, token); |
| pthread_mutex_unlock(&free_list_mutex_); |
| } else { |
| req = free_list_.front(); |
| free_list_.pop(); |
| pthread_mutex_unlock(&free_list_mutex_); |
| req->Set(request, buffer, token); |
| } |
| // add the request |
| Add(req); |
| } else { |
| DBG("RilRequestWorkerQueue:AddRequest: return from the serialization, status is not OK"); |
| // An error report complete now |
| RIL_Errno rilErrCode = (status == STATUS_UNSUPPORTED_REQUEST) ? |
| RIL_E_REQUEST_NOT_SUPPORTED : RIL_E_GENERIC_FAILURE; |
| s_rilenv->OnRequestComplete(token, rilErrCode, NULL, 0); |
| } |
| |
| DBG("RilRequestWorkerQueue::AddRequest: X" |
| " request=%d data=%p datalen=%d token=%p", |
| request, data, datalen, token); |
| } |
| |
| void RilRequestWorkerQueue::Process(void *p) { |
| |
| Request *req = (Request *)p; |
| DBG("RilRequestWorkerQueue::Process: E" |
| " request=%d buffer=%p, bufferlen=%d t=%p", |
| req->request_, req->buffer_, req->buffer_->length(), req->token_); |
| |
| v8::Locker locker; |
| v8::HandleScope handle_scope; |
| v8::Context::Scope context_scope(context_); |
| callOnRilRequest(context_, req->request_, |
| req->buffer_, req->token_); |
| |
| pthread_mutex_lock(&free_list_mutex_); |
| free_list_.push(req); |
| pthread_mutex_unlock(&free_list_mutex_); |
| } |
| |
| int requestsInit(v8::Handle<v8::Context> context, RilRequestWorkerQueue **rwq) { |
| ALOGD("requestsInit E"); |
| |
| rilReqConversionMap[RIL_REQUEST_GET_SIM_STATUS] = ReqWithNoData; // 1 |
| rilReqConversionMap[RIL_REQUEST_ENTER_SIM_PIN] = ReqEnterSimPin; // 2 |
| rilReqConversionMap[RIL_REQUEST_GET_CURRENT_CALLS] = ReqWithNoData; // 9 |
| rilReqConversionMap[RIL_REQUEST_DIAL] = ReqDial; // 10 |
| rilReqConversionMap[RIL_REQUEST_GET_IMSI] = ReqWithNoData; // 11 |
| rilReqConversionMap[RIL_REQUEST_HANGUP] = ReqHangUp; // 12 |
| rilReqConversionMap[RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND] = ReqWithNoData; // 13 |
| rilReqConversionMap[RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND] = ReqWithNoData; // 14 |
| rilReqConversionMap[RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] = ReqWithNoData; // 15 |
| rilReqConversionMap[RIL_REQUEST_CONFERENCE] = ReqWithNoData; // 16 |
| rilReqConversionMap[RIL_REQUEST_LAST_CALL_FAIL_CAUSE] = ReqWithNoData; // 18 |
| rilReqConversionMap[RIL_REQUEST_SIGNAL_STRENGTH] = ReqWithNoData; // 19 |
| rilReqConversionMap[RIL_REQUEST_VOICE_REGISTRATION_STATE] = ReqWithNoData; // 20 |
| rilReqConversionMap[RIL_REQUEST_DATA_REGISTRATION_STATE] = ReqWithNoData; // 21 |
| rilReqConversionMap[RIL_REQUEST_OPERATOR] = ReqWithNoData; // 22 |
| rilReqConversionMap[RIL_REQUEST_GET_IMEI] = ReqWithNoData; // 38 |
| rilReqConversionMap[RIL_REQUEST_GET_IMEISV] = ReqWithNoData; // 39 |
| rilReqConversionMap[RIL_REQUEST_ANSWER] = ReqWithNoData; // 40 |
| rilReqConversionMap[RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE] = ReqWithNoData; // 45 |
| rilReqConversionMap[RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC] = ReqWithNoData; // 46 |
| rilReqConversionMap[RIL_REQUEST_BASEBAND_VERSION] = ReqWithNoData; // 51 |
| rilReqConversionMap[RIL_REQUEST_SEPARATE_CONNECTION] = ReqSeparateConnection; // 52 |
| rilReqConversionMap[RIL_REQUEST_SET_MUTE] = ReqSetMute; // 53 |
| rilReqConversionMap[RIL_REQUEST_SCREEN_STATE] = ReqScreenState; // 61 |
| |
| *rwq = new RilRequestWorkerQueue(context); |
| int status = (*rwq)->Run(); |
| |
| ALOGD("requestsInit X: status=%d", status); |
| return status; |
| } |
| |
| /** |
| * Subroutine to test a single RIL request |
| */ |
| void testRilRequest(v8::Handle<v8::Context> context, int request, const void *data, |
| const size_t datalen, const RIL_Token t) { |
| Buffer *buffer = NULL; |
| ReqConversionMap::iterator itr; |
| int status; |
| |
| ALOGD("testRilRequest: request=%d", request); |
| |
| itr = rilReqConversionMap.find(request); |
| if (itr != rilReqConversionMap.end()) { |
| status = itr->second(&buffer, data, sizeof(data), (void *)0x12345677); |
| } else { |
| ALOGE("testRequests X unknown request %d", request); |
| status = STATUS_UNSUPPORTED_REQUEST; |
| } |
| if (status == STATUS_OK) { |
| callOnRilRequest(context, request, buffer, (void *)0x12345677); |
| } else { |
| ALOGE("testRilRequest X, serialize error"); |
| } |
| } |
| |
| void testRequests(v8::Handle<v8::Context> context) { |
| ALOGD("testRequests E: ********"); |
| |
| v8::TryCatch try_catch; |
| |
| char *buffer; |
| const char *fileName= "/sdcard/data/mock_ril.js"; |
| int status = ReadFile(fileName, &buffer); |
| if (status == 0) { |
| runJs(context, &try_catch, fileName, buffer); |
| Buffer *buffer = NULL; |
| ReqConversionMap::iterator itr; |
| int status; |
| int request; |
| |
| if (!try_catch.HasCaught()) { |
| { |
| const int data[1] = { 1 }; |
| testRilRequest(context, RIL_REQUEST_SIGNAL_STRENGTH, data, sizeof(data), |
| (void *)0x12345677); |
| } |
| { |
| const char *data[1] = { "winks-pin" }; |
| testRilRequest(context, RIL_REQUEST_ENTER_SIM_PIN, data, sizeof(data), |
| (void *)0x12345677); |
| } |
| { |
| const int data[1] = { 1 }; |
| testRilRequest(context, RIL_REQUEST_HANGUP, data, sizeof(data), |
| (void *)0x12345677); |
| } |
| { |
| const int data[1] = { 1 }; |
| testRilRequest(context, RIL_REQUEST_SCREEN_STATE, data, sizeof(data), |
| (void *)0x12345677); |
| } |
| { |
| const int data[1] = { 1 }; |
| testRilRequest(context, RIL_REQUEST_GET_SIM_STATUS, data, sizeof(data), |
| (void *)0x12345677); |
| } |
| { |
| RilRequestWorkerQueue *rwq = new RilRequestWorkerQueue(context); |
| if (rwq->Run() == STATUS_OK) { |
| const int data[1] = { 1 }; |
| rwq->AddRequest(RIL_REQUEST_SCREEN_STATE, |
| data, sizeof(data), (void *)0x1234567A); |
| rwq->AddRequest(RIL_REQUEST_SIGNAL_STRENGTH, |
| data, sizeof(data), (void *)0x1234567A); |
| // Sleep to let it be processed |
| v8::Unlocker unlocker; |
| sleep(3); |
| v8::Locker locker; |
| } |
| delete rwq; |
| } |
| } |
| } |
| |
| ALOGD("testRequests X: ********\n"); |
| } |