Merge change 1820

* changes:
  ACC ARM codegen: Implement calling indirect functions.
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index a47f2e4..6ed5514 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -49,6 +49,7 @@
 #define AID_MEDIA         1013  /* mediaserver process */
 #define AID_DHCP          1014  /* dhcp client */
 #define AID_SDCARD_RW     1015  /* external storage write access */
+#define AID_VPN           1016  /* vpn system */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
@@ -95,6 +96,7 @@
     { "net_bt_admin", AID_NET_BT_ADMIN, },
     { "net_bt",    AID_NET_BT, },
     { "sdcard_rw", AID_SDCARD_RW, },
+    { "vpn",       AID_VPN, },
     { "inet",      AID_INET, }, 
     { "net_raw",   AID_NET_RAW, },
     { "misc",      AID_MISC, },
diff --git a/include/sysutils/ServiceManager.h b/include/sysutils/ServiceManager.h
new file mode 100644
index 0000000..c31dd8f
--- /dev/null
+++ b/include/sysutils/ServiceManager.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#ifndef _SERVICE_MANAGER_H
+#define _SERVICE_MANAGER_H
+
+class ServiceManager {
+public:
+    ServiceManager();
+    virtual ~ServiceManager() {}
+
+    int start(const char *name);
+    int stop(const char *name);
+    bool isRunning(const char *name);
+};
+
+#endif
diff --git a/init/devices.c b/init/devices.c
index 8aea772..300faab 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -131,9 +131,9 @@
     { "/dev/qmi0",          0640,   AID_RADIO,      AID_RADIO,      0 },
     { "/dev/qmi1",          0640,   AID_RADIO,      AID_RADIO,      0 },
     { "/dev/qmi2",          0640,   AID_RADIO,      AID_RADIO,      0 },
-
         /* CDMA radio interface MUX */
     { "/dev/ts0710mux",     0640,   AID_RADIO,      AID_RADIO,      1 },
+    { "/dev/tun",           0640,   AID_VPN  ,      AID_VPN,        0 },
     { NULL, 0, 0, 0, 0 },
 };
 
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
index 2f3e106..dd2b32d 100644
--- a/libsysutils/Android.mk
+++ b/libsysutils/Android.mk
@@ -16,6 +16,7 @@
                   src/NetlinkEvent.cpp        \
                   src/FrameworkCommand.cpp    \
                   src/SocketClient.cpp        \
+                  src/ServiceManager.cpp      \
 
 LOCAL_MODULE:= libsysutils
 
diff --git a/libsysutils/src/ServiceManager.cpp b/libsysutils/src/ServiceManager.cpp
new file mode 100644
index 0000000..700ac91
--- /dev/null
+++ b/libsysutils/src/ServiceManager.cpp
@@ -0,0 +1,73 @@
+#include <errno.h>
+
+#include <sysutils/ServiceManager.h>
+
+#define LOG_TAG "Service"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+ServiceManager::ServiceManager() {
+}
+
+int ServiceManager::start(const char *name) {
+    if (isRunning(name)) {
+        LOGW("Service '%s' is already running", name);
+        return 0;
+    }
+
+    LOGD("Starting service '%s'", name);
+    property_set("ctl.start", name);
+
+    int count = 200;
+    while(count--) {
+        sched_yield();
+        if (isRunning(name))
+            break;
+    }
+    if (!count) {
+        LOGW("Timed out waiting for service '%s' to start", name);
+        errno = ETIMEDOUT;
+        return -1;
+    }
+    LOGD("Sucessfully started '%s'", name);
+    return 0;
+}
+
+int ServiceManager::stop(const char *name) {
+    if (!isRunning(name)) {
+        LOGW("Service '%s' is already stopped", name);
+        return 0;
+    }
+
+    LOGD("Stopping service '%s'", name);
+    property_set("ctl.stop", name);
+
+    int count = 200;
+    while(count--) {
+        sched_yield();
+        if (!isRunning(name))
+            break;
+    }
+
+    if (!count) {
+        LOGW("Timed out waiting for service '%s' to stop", name);
+        errno = ETIMEDOUT;
+        return -1;
+    }
+    LOGD("Sucessfully stopped '%s'", name);
+    return 0;
+}
+
+bool ServiceManager::isRunning(const char *name) {
+    char propVal[PROPERTY_VALUE_MAX];
+    char propName[255];
+
+    snprintf(propName, sizeof(propVal), "init.svc.%s", name);
+
+
+    if (property_get(propName, propVal, NULL)) {
+        if (!strcmp(propVal, "running"))
+            return true;
+    }
+    return false;
+}
diff --git a/nexus/CommandListener.cpp b/nexus/CommandListener.cpp
index d9ee971..fdf3fed 100644
--- a/nexus/CommandListener.cpp
+++ b/nexus/CommandListener.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 #include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 #include <errno.h>
 
 #define LOG_TAG "CommandListener"
@@ -25,6 +28,7 @@
 #include "Controller.h"
 #include "NetworkManager.h"
 #include "WifiController.h"
+#include "VpnController.h"
 #include "ErrorCode.h"
 
 CommandListener::CommandListener() :
@@ -40,6 +44,8 @@
     registerCmd(new WifiGetVarCmd());
 
     registerCmd(new VpnEnableCmd());
+    registerCmd(new VpnSetVarCmd());
+    registerCmd(new VpnGetVarCmd());
     registerCmd(new VpnDisableCmd());
 }
  
@@ -261,6 +267,79 @@
     return 0;
 }
 
+CommandListener::VpnSetVarCmd::VpnSetVarCmd() :
+                 NexusCommand("vpn_setvar") {
+} 
+
+int CommandListener::VpnSetVarCmd::runCommand(SocketClient *cli, char *data) {
+    VpnController *vc = (VpnController *) NetworkManager::Instance()->findController("VPN");
+
+    char *bword;
+    char *last;
+    char varname[32];
+    char val[250];
+
+    if (!(bword = strtok_r(data, ":", &last)))
+        goto out_inval;
+
+    strncpy(varname, bword, sizeof(varname));
+
+    if (!(bword = strtok_r(NULL, ":", &last)))
+        goto out_inval;
+
+    strncpy(val, bword, sizeof(val));
+
+    if (!strcasecmp(varname, "vpn_gateway")) {
+        if (vc->setVpnGateway(val))
+            goto out_inval;
+    } else {
+        cli->sendMsg(ErrorCode::CommandParameterError, "Variable not found.", true);
+        return 0;
+    }
+
+    cli->sendMsg(ErrorCode::CommandOkay, "Variable written.", false);
+    return 0;
+
+out_inval:
+    errno = EINVAL;
+    cli->sendMsg(ErrorCode::CommandParameterError, "Failed to set variable.", true);
+    return 0;
+}
+
+CommandListener::VpnGetVarCmd::VpnGetVarCmd() :
+                 NexusCommand("vpn_getvar") {
+} 
+
+int CommandListener::VpnGetVarCmd::runCommand(SocketClient *cli, char *data) {
+    VpnController *vc = (VpnController *) NetworkManager::Instance()->findController("VPN");
+
+    char *bword;
+    char *last;
+    char varname[32];
+
+    if (!(bword = strtok_r(data, ":", &last)))
+        goto out_inval;
+   
+    strncpy(varname, bword, sizeof(varname));
+
+    if (!strcasecmp(varname, "vpn_gateway")) {
+        char buffer[255];
+
+        sprintf(buffer, "%s:%s", varname, inet_ntoa(vc->getVpnGateway()));
+        cli->sendMsg(ErrorCode::VariableRead, buffer, false);
+    } else {
+        cli->sendMsg(ErrorCode::CommandParameterError, "Variable not found.", true);
+        return 0;
+    }
+
+    cli->sendMsg(ErrorCode::CommandOkay, "Variable read.", false);
+    return 0;
+out_inval:
+    errno = EINVAL;
+    cli->sendMsg(ErrorCode::CommandParameterError, "Failed to get variable.", true);
+    return 0;
+}
+
 CommandListener::VpnDisableCmd::VpnDisableCmd() :
                  NexusCommand("vpn_disable") {
 } 
diff --git a/nexus/CommandListener.h b/nexus/CommandListener.h
index 7bc89e3..a44aa29 100644
--- a/nexus/CommandListener.h
+++ b/nexus/CommandListener.h
@@ -95,6 +95,20 @@
         int runCommand(SocketClient *c, char *data);
     };
 
+    class VpnSetVarCmd : public NexusCommand {
+    public:
+        VpnSetVarCmd();
+        virtual ~VpnSetVarCmd() {}
+        int runCommand(SocketClient *c, char *data);
+    };
+
+    class VpnGetVarCmd : public NexusCommand {
+    public:
+        VpnGetVarCmd();
+        virtual ~VpnGetVarCmd() {}
+        int runCommand(SocketClient *c, char *data);
+    };
+
     class VpnDisableCmd : public NexusCommand {
     public:
         VpnDisableCmd();
diff --git a/nexus/ErrorCode.h b/nexus/ErrorCode.h
index 8ca6cae..57c99c2 100644
--- a/nexus/ErrorCode.h
+++ b/nexus/ErrorCode.h
@@ -26,6 +26,9 @@
     static const int WifiScanResult = 125;
     static const int WifiNetworkList = 126;
 
+    static const int VariableRead = 127;
+    static const int VariableWrite = 128;
+
     // 200 series - Requested action has been successfully completed
     static const int CommandOkay = 200;
 
diff --git a/nexus/OpenVpnController.cpp b/nexus/OpenVpnController.cpp
index eff653a..d326ad5 100644
--- a/nexus/OpenVpnController.cpp
+++ b/nexus/OpenVpnController.cpp
@@ -14,17 +14,27 @@
  * limitations under the License.
  */
 #include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #define LOG_TAG "OpenVpnController"
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
+#include <sysutils/ServiceManager.h>
+
 #include "OpenVpnController.h"
 
 #define DAEMON_PROP_NAME "vpn.openvpn.status"
+#define DAEMON_CONFIG_FILE "/data/misc/openvpn/openvpn.conf"
 
 OpenVpnController::OpenVpnController() :
                    VpnController() {
+    mServiceManager = new ServiceManager();
+}
+
+OpenVpnController::~OpenVpnController() {
+    delete mServiceManager;
 }
 
 int OpenVpnController::start() {
@@ -37,68 +47,32 @@
 
 int OpenVpnController::enable() {
 
-    // Validate configuration file
-   
-    // Validate key file
-
-    if (startServiceDaemon())
-        return -1;
-
-    errno = -ENOSYS;
-    return -1;
-}
-
-int OpenVpnController::startServiceDaemon() {
-    char status[PROPERTY_VALUE_MAX];
-    int count = 100;
-
-    property_set("ctl.start", "openvpn");
-    sched_yield();
-
-    while (count-- > 0) {
-        if (property_get(DAEMON_PROP_NAME, status, NULL)) {
-            if (strcmp(status, "ok") == 0)
-                return 0;
-            else if (strcmp(DAEMON_PROP_NAME, "failed") == 0)
-                return -1;
-        }
-        usleep(200000);
-    }
-    property_set(DAEMON_PROP_NAME, "timeout");
-    return -1;
-}
-
-int OpenVpnController::stopServiceDaemon() {
-    char status[PROPERTY_VALUE_MAX] = {'\0'};
-    int count = 50;
-
-    if (property_get(DAEMON_PROP_NAME, status, NULL) &&
-        !strcmp(status, "stopped")) {
-        LOGD("Service already stopped");
-        return 0;
-    }
-
-    property_set("ctl.stop", "openvpn");
-    sched_yield();
-
-    while (count-- > 0) {
-        if (property_get(DAEMON_PROP_NAME, status, NULL)) {
-            if (!strcmp(status, "stopped"))
-                break;
-        }
-        usleep(100000);
-    }
-
-    if (!count) {
-        LOGD("Timed out waiting for openvpn to stop");
-        errno = ETIMEDOUT;
+    if (validateConfig()) {
+        LOGE("Error validating configuration file");
         return -1;
     }
 
+    if (mServiceManager->start("openvpn"))
+        return -1;
+
     return 0;
 }
 
 int OpenVpnController::disable() {
-    errno = -ENOSYS;
-    return -1;
+
+    if (mServiceManager->stop("openvpn"))
+        return -1;
+    return 0;
+}
+
+int OpenVpnController::validateConfig() {
+    unlink(DAEMON_CONFIG_FILE);
+
+    FILE *fp = fopen(DAEMON_CONFIG_FILE, "w");
+    if (!fp)
+        return -1;
+
+    fprintf(fp, "remote %s 1194\n", inet_ntoa(getVpnGateway()));
+    fclose(fp);
+    return 0;
 }
diff --git a/nexus/OpenVpnController.h b/nexus/OpenVpnController.h
index 1ecc3fb..7bcc098 100644
--- a/nexus/OpenVpnController.h
+++ b/nexus/OpenVpnController.h
@@ -19,11 +19,15 @@
 
 #include "VpnController.h"
 
+class ServiceManager;
+
 class OpenVpnController : public VpnController {
+private:
+    ServiceManager *mServiceManager;
 
 public:
     OpenVpnController();
-    virtual ~OpenVpnController() {}
+    virtual ~OpenVpnController();
 
     int start();
     int stop();
@@ -33,8 +37,7 @@
 protected:
 
 private:
-    int startServiceDaemon();
-    int stopServiceDaemon();
+    int validateConfig();
 };
 
 #endif
diff --git a/nexus/Supplicant.cpp b/nexus/Supplicant.cpp
index 22964bb..6737d91 100644
--- a/nexus/Supplicant.cpp
+++ b/nexus/Supplicant.cpp
@@ -25,12 +25,7 @@
 
 #include "private/android_filesystem_config.h"
 
-#undef HAVE_LIBC_SYSTEM_PROPERTIES
-
-#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-#endif
+#include <sysutils/ServiceManager.h>
 
 #include "Supplicant.h"
 #include "SupplicantListener.h"
@@ -44,12 +39,10 @@
 
 #define IFACE_DIR        "/data/system/wpa_supplicant"
 #define DRIVER_PROP_NAME "wlan.driver.status"
-#define SUPPLICANT_NAME  "wpa_supplicant"
-#define SUPP_PROP_NAME   "init.svc.wpa_supplicant"
+#define SUPPLICANT_SERVICE_NAME  "wpa_supplicant"
 #define SUPP_CONFIG_TEMPLATE "/system/etc/wifi/wpa_supplicant.conf"
 #define SUPP_CONFIG_FILE "/data/misc/wifi/wpa_supplicant.conf"
 
-
 Supplicant::Supplicant() {
     mCtrl = NULL;
     mMonitor = NULL;
@@ -57,62 +50,25 @@
 
     mState = SupplicantState::UNKNOWN;
 
-    mLatestScanResults = new ScanResultCollection();
+    mServiceManager = new ServiceManager();
 
+    mLatestScanResults = new ScanResultCollection();
     pthread_mutex_init(&mLatestScanResultsLock, NULL);
 }
 
+Supplicant::~Supplicant() {
+    delete mServiceManager;
+}
+
 int Supplicant::start() {
 
     if (setupConfig()) {
         LOGW("Unable to setup supplicant.conf");
     }
-    
-    char status[PROPERTY_VALUE_MAX] = {'\0'};
-    int count = 200;
-#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
-    const prop_info *pi;
-    unsigned int serial = 0;
-#endif
-
-    if (property_get(SUPP_PROP_NAME, status, NULL) &&
-        !strcmp(status, "running")) {
-    } else {
-#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
-        pi = __system_property_find(SUPP_PROP_NAME);
-        if (pi != NULL)
-            serial = pi->serial;
-#endif
-
-        LOGD("Starting Supplicant");
-        property_set("ctl.start", SUPPLICANT_NAME);
-        sched_yield();
-        while (count--) {
-#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
-            if (!pi)
-                pi = __system_property_find(SUPP_PROP_NAME);
-            if (pi) {
-                __system_property_read(pi, NULL, status);
-                if (strcmp(status, "running") == 0)
-                    break;
-                else if (pi->serial != serial &&
-                        strcmp(status, "stopped") == 0) {
-                    errno = EIO;
-                    return -1;
-                }
-            }
-#else
-            if (property_get(SUPP_PROP_NAME, status, NULL)) {
-                if (!strcmp(status, "running"))
-                    break;
-            }
-#endif
-            usleep(100000);
-        }
-        if (!count) {
-            errno = ETIMEDOUT;
-            return -1;
-        }
+ 
+    if (mServiceManager->start(SUPPLICANT_SERVICE_NAME)) {
+        LOGE("Error starting supplicant (%s)", strerror(errno));
+        return -1;
     }
 
     wpa_ctrl_cleanup();
@@ -125,30 +81,13 @@
 
 int Supplicant::stop() {
 
-    char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
-    int count = 50; 
-
     if (mListener->stopListener()) {
         LOGW("Unable to stop supplicant listener (%s)", strerror(errno));
         return -1;
     }
 
-    if (property_get(SUPP_PROP_NAME, supp_status, NULL)
-        && strcmp(supp_status, "stopped") == 0) {
-        LOGD("Supplicant already stopped");
-        return 0;
-    }
-
-    LOGD("Stopping Supplicant");
-    property_set("ctl.stop", SUPPLICANT_NAME);
-    sched_yield();
-
-    while (count-- > 0) {
-        if (property_get(SUPP_PROP_NAME, supp_status, NULL)) {
-            if (strcmp(supp_status, "stopped") == 0)
-                break;
-        }
-        usleep(100000);
+    if (mServiceManager->stop(SUPPLICANT_SERVICE_NAME)) {
+        LOGW("Error stopping supplicant (%s)", strerror(errno));
     }
 
     if (mCtrl) {
@@ -160,35 +99,15 @@
         mMonitor = NULL;
     }
 
-    if (!count) {
-        LOGD("Timed out waiting for supplicant to stop");
-        errno = ETIMEDOUT;
-        return -1;
-    }
-
-    LOGD("Supplicant shutdown");
-
     return 0;
 }
 
 bool Supplicant::isStarted() {
-    char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
-
-    property_get(SUPP_PROP_NAME, supp_status, NULL);
-
-    if (!strcmp(supp_status, "running"))
-        return true;
-
-    return false;
+    return mServiceManager->isRunning(SUPPLICANT_SERVICE_NAME);
 }
 
 int Supplicant::connectToSupplicant() {
-    char ifname[256];
-    char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
-
-    LOGD("connectToSupplicant()");
-    if (!property_get(SUPP_PROP_NAME, supp_status, NULL)
-            || strcmp(supp_status, "running") != 0) {
+    if (!isStarted()) {
         LOGE("Supplicant not running, cannot connect");
         return -1;
     }
@@ -229,7 +148,7 @@
         return -1;
     }
 
-    LOGD("sendCommand(): -> '%s'", cmd);
+//    LOGD("sendCommand(): -> '%s'", cmd);
 
     int rc;
     if ((rc = wpa_ctrl_request(mCtrl, cmd, strlen(cmd), reply, reply_len, NULL)) == -2)  {
@@ -245,7 +164,7 @@
         !strncmp(cmd, "SCAN_RESULTS", 12)) 
         reply[*reply_len] = '\0';
 
-    LOGD("sendCommand(): <- '%s'", reply);
+//    LOGD("sendCommand(): <- '%s'", reply);
     return 0;
 }
 
diff --git a/nexus/Supplicant.h b/nexus/Supplicant.h
index 4a7ec3a..2ea0892 100644
--- a/nexus/Supplicant.h
+++ b/nexus/Supplicant.h
@@ -19,6 +19,7 @@
 struct wpa_ctrl;
 class SupplicantListener;
 class SupplicantEvent;
+class ServiceManager;
 
 #include <pthread.h>
 
@@ -31,13 +32,14 @@
     struct wpa_ctrl      *mMonitor;
     SupplicantListener   *mListener;
     int                  mState;
+    ServiceManager       *mServiceManager;
 
     ScanResultCollection *mLatestScanResults;
     pthread_mutex_t      mLatestScanResultsLock;
   
 public:
     Supplicant();
-    virtual ~Supplicant() {}
+    virtual ~Supplicant();
 
     int start();
     int stop();
@@ -50,7 +52,6 @@
     int removeNetwork(int networkId);
     WifiNetworkCollection *createNetworkList();
 
-
     int getState() { return mState; }
 
 
diff --git a/nexus/VpnController.cpp b/nexus/VpnController.cpp
index 17bfe41..cd24c19 100644
--- a/nexus/VpnController.cpp
+++ b/nexus/VpnController.cpp
@@ -13,7 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include <string.h>
 #include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #include "VpnController.h"
 
 VpnController::VpnController() :
@@ -21,21 +27,34 @@
 }
 
 int VpnController::start() {
-    errno = -ENOSYS;
+    errno = ENOSYS;
     return -1;
 }
 
 int VpnController::stop() {
-    errno = -ENOSYS;
+    errno = ENOSYS;
     return -1;
 }
 
 int VpnController::enable() {
-    errno = -ENOSYS;
+    errno = ENOSYS;
     return -1;
 }
 
 int VpnController::disable() {
-    errno = -ENOSYS;
+    errno = ENOSYS;
     return -1;
 }
+
+int VpnController::setVpnGateway(const char *vpnGw) {
+    if (!inet_aton(vpnGw, &mVpnGateway)) {
+        errno = EINVAL;
+        return -1;
+    }
+    return 0;
+}
+
+int VpnController::setVpnGateway(struct in_addr *vpnGw) {
+    memcpy(&mVpnGateway, vpnGw, sizeof(struct in_addr));
+    return 0;
+}
diff --git a/nexus/VpnController.h b/nexus/VpnController.h
index 049fe6e..4088e6a 100644
--- a/nexus/VpnController.h
+++ b/nexus/VpnController.h
@@ -16,9 +16,15 @@
 #ifndef _VPN_CONTROLLER_H
 #define _VPN_CONTROLLER_H
 
+#include <netinet/in.h>
+
 #include "Controller.h"
 
 class VpnController : public Controller {
+    /*
+     * Gateway of the VPN server to connect to
+     */
+    struct in_addr mVpnGateway;
 
 public:
     VpnController();
@@ -30,6 +36,10 @@
     virtual int enable();
     virtual int disable();
 
+    struct in_addr &getVpnGateway() { return mVpnGateway; }
+    int setVpnGateway(const char *vpnGw);
+    int setVpnGateway(struct in_addr *vpnGw);
+
 protected:
 };