Create a very initial mock_ril.js and supporting code.
Change-Id: I742c413f33d99e8fe8e5a320dab067174b560e98
diff --git a/mock-ril/ctrl_server.cpp b/mock-ril/ctrl_server.cpp
new file mode 100644
index 0000000..65338ca
--- /dev/null
+++ b/mock-ril/ctrl_server.cpp
@@ -0,0 +1,482 @@
+/**
+ * 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
+}