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