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
+}