blob: 65338ca903a44dd16cd387fe1673c248800f0744 [file] [log] [blame]
/**
* 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 <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/endian.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cutils/sockets.h>
#include "logging.h"
#include "node_buffer.h"
#include "status.h"
#include "util.h"
#include "worker.h"
#include "ctrl.pb.h"
#include "ctrl_server.h"
//#define CONTROL_SERVER_DEBUG
#ifdef CONTROL_SERVER_DEBUG
#define DBG(...) LOGD(__VA_ARGS__)
#else
#define DBG(...)
#endif
#define MOCK_RIL_CONTROL_SERVER_STOPPING_SOCKET 54311
#define MOCK_RIL_CONTROL_SERVER_SOCKET 54312
class CtrlServerThread;
static CtrlServerThread *g_ctrl_server;
class CtrlServerThread : public WorkerThread {
private:
#define SOCKET_NAME_MOCK_RIL_CST_STOPPER "mock-ril-cst-stopper"
v8::Handle<v8::Context> context_;
int server_accept_socket_;
int server_to_client_socket_;
int stop_server_fd_;
int stop_client_fd_;
int stopper_fd_;
fd_set rd_fds_;
fd_set wr_fds_;
bool done_;
Buffer *ObtainBuffer(int length) {
Buffer *b = Buffer::New(length);
return b;
}
int WriteAll(int s, void *data, int length) {
int ret_value;
uint8_t *bytes = (uint8_t *)data;
int count = length;
while (length > 0) {
ret_value = send(s, bytes, length, 0);
if (ret_value < 0) {
return STATUS_ERR;
}
if (ret_value == 0) {
return STATUS_CLIENT_CLOSED_CONNECTION;
}
bytes += ret_value;
length -= ret_value;
}
return STATUS_OK;
}
int ReadAll(int s, void *data, int length) {
int ret_value;
uint8_t *bytes = (uint8_t *)data;
int count = length;
while (length != 0) {
ret_value = recv(s, bytes, length, 0);
if (ret_value < 0) {
return STATUS_ERR;
}
if (ret_value == 0) {
return STATUS_CLIENT_CLOSED_CONNECTION;
}
bytes += ret_value;
length -= ret_value;
}
return STATUS_OK;
}
int ReadMessage(MsgHeader *mh, Buffer **pBuffer) {
int status;
status = ReadAll(server_to_client_socket_, &mh->cmd, 4);
mh->cmd = letoh32(mh->cmd);
DBG("rm: mh->cmd=%d status=%d", mh->cmd, status);
if (status != STATUS_OK) return status;
status = ReadAll(server_to_client_socket_, &mh->token, 8);
mh->token = letoh64(mh->token);
DBG("rm: mh->token=%lld status=%d", mh->token, status);
if (status != STATUS_OK) return status;
status = ReadAll(server_to_client_socket_, &mh->status, 4);
mh->status = letoh32(mh->status);
DBG("rm: mh->status=%d status=%d", mh->status, status);
if (status != STATUS_OK) return status;
status = ReadAll(server_to_client_socket_, &mh->length_protobuf, 4);
mh->length_protobuf = letoh32(mh->length_protobuf);
DBG("rm: mh->length_protobuf=%d status=%d", mh->length_protobuf, status);
if (status != STATUS_OK) return status;
Buffer *buffer;
if (mh->length_protobuf > 0) {
buffer = ObtainBuffer(mh->length_protobuf);
status = ReadAll(server_to_client_socket_, buffer->data(), buffer->length());
DBG("rm: read protobuf status=%d", status);
if (status != STATUS_OK) return status;
} else {
DBG("rm: NO protobuf");
mh->length_protobuf = 0;
buffer = NULL;
}
*pBuffer = buffer;
return STATUS_OK;
}
public:
int WriteMessage(MsgHeader *mh, Buffer *buffer) {
int status;
uint32_t i;
uint64_t l;
i = htole32(mh->cmd);
status = WriteAll(server_to_client_socket_, &i, 4);
DBG("wm: mh->cmd=%d status=%d", mh->cmd, status);
if (status != 0) return status;
l = htole64(mh->token);
status = WriteAll(server_to_client_socket_, &l, 8);
DBG("wm: mh->token=%lld status=%d", mh->token, status);
if (status != 0) return status;
i = htole32(mh->status);
status = WriteAll(server_to_client_socket_, &i, 4);
DBG("wm: mh->status=%d status=%d", mh->status, status);
if (status != 0) return status;
if (buffer == NULL) {
mh->length_protobuf = 0;
} else {
mh->length_protobuf = buffer->length();
}
if (mh->length_protobuf < 0) {
LOGE("wm: length_protobuf=zero");
mh->length_protobuf = 0;
}
i = htole32(mh->length_protobuf);
status = WriteAll(server_to_client_socket_, &i, 4);
DBG("wm: mh->length_protobuf=%d status=%d",
mh->length_protobuf, status);
if (status != 0) return status;
if (mh->length_protobuf > 0) {
status = WriteAll(server_to_client_socket_, buffer->data(), buffer->length());
DBG("wm: protobuf data=%p len=%d status=%d",
buffer->data(), buffer->length(), status);
if (status != 0) return status;
}
return STATUS_OK;
}
CtrlServerThread(v8::Handle<v8::Context> context) :
context_(context),
server_accept_socket_(-1),
server_to_client_socket_(-1),
done_(false) {
}
virtual int Run() {
DBG("CtrlServerThread::Run E");
// Create a server socket.
server_accept_socket_ = socket_inaddr_any_server(
MOCK_RIL_CONTROL_SERVER_SOCKET, SOCK_STREAM);
if (server_accept_socket_ < 0) {
LOGE("CtrlServerThread::Run error creating server_accept_socket_ '%s'",
strerror(errno));
return STATUS_ERR;
}
// Create a server socket that will be used for stopping
stop_server_fd_ = socket_loopback_server(
MOCK_RIL_CONTROL_SERVER_STOPPING_SOCKET, SOCK_STREAM);
if (stop_server_fd_ < 0) {
LOGE("CtrlServerThread::Run error creating stop_server_fd_ '%s'",
strerror(errno));
return STATUS_ERR;
}
// Create a client socket that will be used for sending a stop
stop_client_fd_ = socket_loopback_client(
MOCK_RIL_CONTROL_SERVER_STOPPING_SOCKET, SOCK_STREAM);
if (stop_client_fd_ < 0) {
LOGE("CtrlServerThread::Run error creating stop_client_fd_ '%s'",
strerror(errno));
return STATUS_ERR;
}
// Accept the connection of the stop_client_fd_
stopper_fd_ = accept(stop_server_fd_, NULL, NULL);
if (stopper_fd_ < 0) {
LOGE("CtrlServerThread::Run error accepting stop_client_fd '%s'",
strerror(errno));
return STATUS_ERR;
}
// Run the new thread
int ret_value = WorkerThread::Run(NULL);
DBG("CtrlServerThread::Run X");
return ret_value;
}
virtual void Stop() {
DBG("CtrlServerThread::Stop E");
if (BeginStopping()) {
done_ = true;
int rv = send(stop_client_fd_, &done_, sizeof(done_), 0);
if (rv <= 0) {
LOGE("CtrlServerThread::Stop could not send stop"
"WE WILL PROBABLY HANG");
}
WaitUntilStopped();
}
DBG("CtrlServerThread::Stop X");
}
virtual bool isRunning() {
bool rv = done_ || WorkerThread::isRunning();
return rv;
}
int WaitOnSocketOrStopping(fd_set *rfds, int s) {
DBG("WaitOnSocketOrStopping E s=%d stopper_fd_=%d", s, stopper_fd_);
FD_ZERO(rfds);
FD_SET(s, rfds);
FD_SET(stopper_fd_, rfds);
int fd_number = s > stopper_fd_ ? s + 1 : stopper_fd_ + 1;
v8::Unlocker unlocker;
int rv = select(fd_number, rfds, NULL, NULL, NULL);
v8::Locker locker;
DBG("WaitOnSocketOrStopping X rv=%d s=%d stopper_fd_=%d", rv, s, stopper_fd_);
return rv;
}
int sendToCtrlServer(MsgHeader *mh, Buffer *buffer) {
DBG("sendToCtrlServer E: cmd=%d token=%lld", mh->cmd, mh->token);
int status = STATUS_OK;
v8::HandleScope handle_scope;
v8::TryCatch try_catch;
try_catch.SetVerbose(true);
// Get the onRilRequest Function
v8::Handle<v8::String> name = v8::String::New("onCtrlServerCmd");
v8::Handle<v8::Value> onCtrlServerCmdFunctionValue =
context_->Global()->Get(name);
v8::Handle<v8::Function> onCtrlServerCmdFunction =
v8::Handle<v8::Function>::Cast(onCtrlServerCmdFunctionValue);
// Create the CmdValue and TokenValue
v8::Handle<v8::Value> v8CmdValue = v8::Number::New(mh->cmd);
v8::Handle<v8::Value> v8TokenValue = v8::Number::New(mh->token);
// Invoke onRilRequest
const int argc = 3;
v8::Handle<v8::Value> buf;
if (mh->length_protobuf == 0) {
buf = v8::Undefined();
} else {
buf = buffer->handle_;
}
v8::Handle<v8::Value> argv[argc] = {
v8CmdValue, v8TokenValue, buf };
v8::Handle<v8::Value> result =
onCtrlServerCmdFunction->Call(context_->Global(), argc, argv);
if (try_catch.HasCaught()) {
ReportException(&try_catch);
status = STATUS_ERR;
} else {
v8::String::Utf8Value result_string(result);
DBG("sendToCtrlServer result=%s", ToCString(result_string));
status = STATUS_OK;
}
if (status != STATUS_OK) {
LOGE("sendToCtrlServer Error: status=%d", status);
// An error report complete now
mh->length_protobuf = 0;
mh->status = ctrl_proto::CTRL_STATUS_ERR;
g_ctrl_server->WriteMessage(mh, NULL);
}
DBG("sendToCtrlServer X: status=%d", status);
return status;
}
virtual void * Worker(void *param) {
DBG("CtrlServerThread::Worker E param=%p stopper_fd_=%d",
param, stopper_fd_);
v8::Locker locker;
v8::HandleScope handle_scope;
v8::Context::Scope context_scope(context_);
while (isRunning()) {
int ret_value;
// Wait on either server_accept_socket_ or stopping
DBG("CtrlServerThread::Worker wait on server for a client");
WaitOnSocketOrStopping(&rd_fds_, server_accept_socket_);
if (isRunning() != true) {
break;
}
if (FD_ISSET(server_accept_socket_, &rd_fds_)) {
server_to_client_socket_ = accept(server_accept_socket_, NULL, NULL);
DBG("CtrlServerThread::Worker accepted server_to_client_socket_=%d isRunning()=%d",
server_to_client_socket_, isRunning());
int status;
Buffer *buffer;
MsgHeader mh;
while ((server_to_client_socket_ > 0) && isRunning()) {
DBG("CtrlServerThread::Worker wait on client for message");
WaitOnSocketOrStopping(&rd_fds_, server_to_client_socket_);
if (isRunning() != true) {
break;
}
status = ReadMessage(&mh, &buffer);
if (status != STATUS_OK) break;
if (mh.cmd == ctrl_proto::CTRL_CMD_ECHO) {
LOGD("CtrlServerThread::Worker echo");
status = WriteMessage(&mh, buffer);
if (status != STATUS_OK) break;
} else {
DBG("CtrlServerThread::Worker sendToCtrlServer");
status = sendToCtrlServer(&mh, buffer);
if (status != STATUS_OK) break;
}
}
close(server_to_client_socket_);
server_to_client_socket_ = -1;
}
}
close(stop_server_fd_);
stop_server_fd_ = -1;
close(stop_client_fd_);
stop_client_fd_ = -1;
close(stopper_fd_);
stopper_fd_ = -1;
close(server_accept_socket_);
server_accept_socket_ = -1;
DBG("CtrlServerThread::Worker X param=%p", param);
return NULL;
}
};
/**
* Send a control request complete response.
*/
v8::Handle<v8::Value> SendCtrlRequestComplete(const v8::Arguments& args) {
DBG("SendCtrlRequestComplete E:");
v8::HandleScope handle_scope;
v8::Handle<v8::Value> retValue;
void *data;
size_t datalen;
Buffer* buffer;
MsgHeader mh;
/**
* Get the arguments. There should be at least 3, reqNum,
* ril error code and token. Optionally a Buffer containing
* the protobuf representation of the data to return.
*/
if (args.Length() < 3) {
// Expecting a reqNum, ERROR and token
LOGE("SendCtrlRequestComplete X %d parameters"
" expecting at least 3: status, reqNum, and token",
args.Length());
return v8::Undefined();
}
v8::Handle<v8::Value> v8CtrlStatus(args[0]->ToObject());
mh.status = ctrl_proto::CtrlStatus(v8CtrlStatus->NumberValue());
DBG("SendCtrlRequestComplete: status=%d", mh.status);
v8::Handle<v8::Value> v8ReqNum(args[1]->ToObject());
mh.cmd = int(v8ReqNum->NumberValue());
DBG("SendCtrlRequestComplete: cmd=%d", mh.cmd);
v8::Handle<v8::Value> v8Token(args[2]->ToObject());
mh.token = int64_t(v8Token->NumberValue());
DBG("SendCtrlRequestComplete: token=%lld", mh.token);
if (args.Length() >= 4) {
buffer = ObjectWrap::Unwrap<Buffer>(args[3]->ToObject());
mh.length_protobuf = buffer->length();
DBG("SendCtrlRequestComplete: mh.length_protobuf=%d",
mh.length_protobuf);
} else {
mh.length_protobuf = 0;
buffer = NULL;
DBG("SendCtrlRequestComplete: NO PROTOBUF");
}
DBG("SendCtrlRequestComplete: WriteMessage");
int status = g_ctrl_server->WriteMessage(&mh, buffer);
DBG("SendCtrlRequestComplete E:");
return v8::Undefined();
}
void ctrlServerInit(v8::Handle<v8::Context> context) {
int status;
g_ctrl_server = new CtrlServerThread(context);
status = g_ctrl_server->Run();
if (status != STATUS_OK) {
LOGE("mock_ril control server could not start");
} else {
LOGD("CtrlServer started");
}
#if 0
LOGD("Test CtrlServerThread stop sleeping 10 seconds...");
v8::Unlocker unlocker;
sleep(10);
LOGD("Test CtrlServerThread call Stop");
g_ctrl_server->Stop();
v8::Locker locker;
// Restart
g_ctrl_server = new CtrlServerThread(context);
status = g_ctrl_server->Run();
if (status != STATUS_OK) {
LOGE("mock_ril control server could not start");
} else {
DBG("mock_ril control server started");
}
#endif
}