--- /dev/null
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define LOG_TAG "Supplicant"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "private/android_filesystem_config.h"
+
+#include <sysutils/ServiceManager.h>
+
+#include "Supplicant.h"
+#include "SupplicantListener.h"
+#include "NetworkManager.h"
+#include "WifiController.h"
+#include "SupplicantStatus.h"
+
+#include "libwpa_client/wpa_ctrl.h"
+
+#define IFACE_DIR "/data/system/wpa_supplicant"
+#define DRIVER_PROP_NAME "wlan.driver.status"
+#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(WifiController *wc, ISupplicantEventHandler *handlers) {
+ mHandlers = handlers;
+ mController = wc;
+ mInterfaceName = NULL;
+ mCtrl = NULL;
+ mMonitor = NULL;
+ mListener = NULL;
+
+ mServiceManager = new ServiceManager();
+
+ mNetworks = new WifiNetworkCollection();
+ pthread_mutex_init(&mNetworksLock, NULL);
+}
+
+Supplicant::~Supplicant() {
+ delete mServiceManager;
+ if (mInterfaceName)
+ free(mInterfaceName);
+}
+
+int Supplicant::start() {
+
+ if (setupConfig()) {
+ LOGW("Unable to setup supplicant.conf");
+ }
+
+ if (mServiceManager->start(SUPPLICANT_SERVICE_NAME)) {
+ LOGE("Error starting supplicant (%s)", strerror(errno));
+ return -1;
+ }
+
+ wpa_ctrl_cleanup();
+ if (connectToSupplicant()) {
+ LOGE("Error connecting to supplicant (%s)\n", strerror(errno));
+ return -1;
+ }
+
+ if (retrieveInterfaceName()) {
+ LOGE("Error retrieving interface name (%s)\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int Supplicant::stop() {
+
+ if (mListener->stopListener()) {
+ LOGW("Unable to stop supplicant listener (%s)", strerror(errno));
+ return -1;
+ }
+
+ if (mServiceManager->stop(SUPPLICANT_SERVICE_NAME)) {
+ LOGW("Error stopping supplicant (%s)", strerror(errno));
+ }
+
+ if (mCtrl) {
+ wpa_ctrl_close(mCtrl);
+ mCtrl = NULL;
+ }
+ if (mMonitor) {
+ wpa_ctrl_close(mMonitor);
+ mMonitor = NULL;
+ }
+
+ return 0;
+}
+
+bool Supplicant::isStarted() {
+ return mServiceManager->isRunning(SUPPLICANT_SERVICE_NAME);
+}
+
+int Supplicant::sendCommand(const char *cmd, char *reply, size_t *reply_len) {
+
+ if (!mCtrl) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+// LOGD("sendCommand(): -> '%s'", cmd);
+
+ int rc;
+ memset(reply, 0, *reply_len);
+ if ((rc = wpa_ctrl_request(mCtrl, cmd, strlen(cmd), reply, reply_len, NULL)) == -2) {
+ errno = ETIMEDOUT;
+ return -1;
+ } else if (rc < 0 || !strncmp(reply, "FAIL", 4)) {
+ strcpy(reply, "FAIL");
+ errno = EIO;
+ return -1;
+ }
+
+ // LOGD("sendCommand(): <- '%s'", reply);
+ return 0;
+}
+SupplicantStatus *Supplicant::getStatus() {
+ char *reply;
+ size_t len = 4096;
+
+ if (!(reply = (char *) malloc(len))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ if (sendCommand("STATUS", reply, &len)) {
+ free(reply);
+ return NULL;
+ }
+
+ SupplicantStatus *ss = SupplicantStatus::createStatus(reply, len);
+
+ free (reply);
+ return ss;
+}
+
+/*
+ * Retrieves the list of networks from Supplicant
+ * and merge them into our current list
+ */
+int Supplicant::refreshNetworkList() {
+ char *reply;
+ size_t len = 4096;
+
+ if (!(reply = (char *) malloc(len))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (sendCommand("LIST_NETWORKS", reply, &len)) {
+ free(reply);
+ return -1;
+ }
+
+ char *linep;
+ char *linep_next = NULL;
+
+ if (!strtok_r(reply, "\n", &linep_next)) {
+ LOGW("Malformatted network list\n");
+ free(reply);
+ errno = EIO;
+ return -1;
+ }
+
+ PropertyManager *pm = NetworkManager::Instance()->getPropMngr();
+ pthread_mutex_lock(&mNetworksLock);
+
+ int num_added = 0;
+ int num_refreshed = 0;
+ int num_removed = 0;
+ while((linep = strtok_r(NULL, "\n", &linep_next))) {
+ // TODO: Move the decode into a static method so we
+ // don't create new_wn when we don't have to.
+ WifiNetwork *new_wn = new WifiNetwork(mController, this, linep);
+ WifiNetwork *merge_wn;
+
+ if ((merge_wn = this->lookupNetwork_UNLOCKED(new_wn->getNetworkId()))) {
+ num_refreshed++;
+ if (merge_wn->refresh()) {
+ LOGW("Error refreshing network %d (%s)",
+ merge_wn->getNetworkId(), strerror(errno));
+ }
+ delete new_wn;
+ } else {
+ num_added++;
+ char new_ns[20];
+ snprintf(new_ns, sizeof(new_ns), "wifi.net.%d", new_wn->getNetworkId());
+ new_wn->attachProperties(pm, new_ns);
+ mNetworks->push_back(new_wn);
+ if (new_wn->refresh()) {
+ LOGW("Unable to refresh network id %d (%s)",
+ new_wn->getNetworkId(), strerror(errno));
+ }
+ }
+ }
+
+ if (!mNetworks->empty()) {
+ // TODO: Add support for detecting removed networks
+ WifiNetworkCollection::iterator i;
+
+ for (i = mNetworks->begin(); i != mNetworks->end(); ++i) {
+ if (0) {
+ num_removed++;
+ char del_ns[20];
+ snprintf(del_ns, sizeof(del_ns), "wifi.net.%d", (*i)->getNetworkId());
+ (*i)->detachProperties(pm, del_ns);
+ delete (*i);
+ i = mNetworks->erase(i);
+ }
+ }
+ }
+
+
+ LOGD("Networks added %d, refreshed %d, removed %d\n",
+ num_added, num_refreshed, num_removed);
+ pthread_mutex_unlock(&mNetworksLock);
+
+ free(reply);
+ return 0;
+}
+
+int Supplicant::connectToSupplicant() {
+ if (!isStarted())
+ LOGW("Supplicant service not running");
+
+ mCtrl = wpa_ctrl_open("tiwlan0"); // XXX:
+ if (mCtrl == NULL) {
+ LOGE("Unable to open connection to supplicant on \"%s\": %s",
+ "tiwlan0", strerror(errno));
+ return -1;
+ }
+ mMonitor = wpa_ctrl_open("tiwlan0");
+ if (mMonitor == NULL) {
+ wpa_ctrl_close(mCtrl);
+ mCtrl = NULL;
+ return -1;
+ }
+ if (wpa_ctrl_attach(mMonitor) != 0) {
+ wpa_ctrl_close(mMonitor);
+ wpa_ctrl_close(mCtrl);
+ mCtrl = mMonitor = NULL;
+ return -1;
+ }
+
+ mListener = new SupplicantListener(mHandlers, mMonitor);
+
+ if (mListener->startListener()) {
+ LOGE("Error - unable to start supplicant listener");
+ stop();
+ return -1;
+ }
+ return 0;
+}
+
+int Supplicant::setScanMode(bool active) {
+ char reply[255];
+ size_t len = sizeof(reply);
+
+ if (sendCommand((active ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"),
+ reply, &len)) {
+ LOGW("triggerScan(%d): Error setting scan mode (%s)", active,
+ strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int Supplicant::triggerScan() {
+ char reply[255];
+ size_t len = sizeof(reply);
+
+ if (sendCommand("SCAN", reply, &len)) {
+ LOGW("triggerScan(): Error initiating scan");
+ return -1;
+ }
+ return 0;
+}
+
+int Supplicant::getRssi(int *buffer) {
+ char reply[64];
+ size_t len = sizeof(reply);
+
+ if (sendCommand("DRIVER RSSI", reply, &len)) {
+ LOGW("Failed to get RSSI (%s)", strerror(errno));
+ return -1;
+ }
+
+ char *next = reply;
+ char *s;
+ for (int i = 0; i < 3; i++) {
+ if (!(s = strsep(&next, " "))) {
+ LOGE("Error parsing RSSI");
+ errno = EIO;
+ return -1;
+ }
+ }
+ *buffer = atoi(s);
+ return 0;
+}
+
+int Supplicant::getLinkSpeed() {
+ char reply[64];
+ size_t len = sizeof(reply);
+
+ if (sendCommand("DRIVER LINKSPEED", reply, &len)) {
+ LOGW("Failed to get LINKSPEED (%s)", strerror(errno));
+ return -1;
+ }
+
+ char *next = reply;
+ char *s;
+
+ if (!(s = strsep(&next, " "))) {
+ LOGE("Error parsing LINKSPEED");
+ errno = EIO;
+ return -1;
+ }
+
+ if (!(s = strsep(&next, " "))) {
+ LOGE("Error parsing LINKSPEED");
+ errno = EIO;
+ return -1;
+ }
+ return atoi(s);
+}
+
+int Supplicant::stopDriver() {
+ char reply[64];
+ size_t len = sizeof(reply);
+
+ LOGD("stopDriver()");
+
+ if (sendCommand("DRIVER STOP", reply, &len)) {
+ LOGW("Failed to stop driver (%s)", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int Supplicant::startDriver() {
+ char reply[64];
+ size_t len = sizeof(reply);
+
+ LOGD("startDriver()");
+ if (sendCommand("DRIVER START", reply, &len)) {
+ LOGW("Failed to start driver (%s)", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+WifiNetwork *Supplicant::createNetwork() {
+ char reply[255];
+ size_t len = sizeof(reply) -1;
+
+ if (sendCommand("ADD_NETWORK", reply, &len))
+ return NULL;
+
+ if (reply[strlen(reply) -1] == '\n')
+ reply[strlen(reply) -1] = '\0';
+
+ WifiNetwork *wn = new WifiNetwork(mController, this, atoi(reply));
+ PropertyManager *pm = NetworkManager::Instance()->getPropMngr();
+ pthread_mutex_lock(&mNetworksLock);
+ char new_ns[20];
+ snprintf(new_ns, sizeof(new_ns), "wifi.net.%d", wn->getNetworkId());
+ wn->attachProperties(pm, new_ns);
+ mNetworks->push_back(wn);
+ pthread_mutex_unlock(&mNetworksLock);
+ return wn;
+}
+
+int Supplicant::removeNetwork(WifiNetwork *wn) {
+ char req[64];
+
+ sprintf(req, "REMOVE_NETWORK %d", wn->getNetworkId());
+ char reply[32];
+ size_t len = sizeof(reply) -1;
+
+ if (sendCommand(req, reply, &len))
+ return -1;
+
+ pthread_mutex_lock(&mNetworksLock);
+ WifiNetworkCollection::iterator it;
+ for (it = mNetworks->begin(); it != mNetworks->end(); ++it) {
+ if ((*it) == wn) {
+ mNetworks->erase(it);
+ break;
+ }
+ }
+ pthread_mutex_unlock(&mNetworksLock);
+ return 0;
+}
+
+WifiNetwork *Supplicant::lookupNetwork(int networkId) {
+ pthread_mutex_lock(&mNetworksLock);
+ WifiNetwork *wn = lookupNetwork_UNLOCKED(networkId);
+ pthread_mutex_unlock(&mNetworksLock);
+ return wn;
+}
+
+WifiNetwork *Supplicant::lookupNetwork_UNLOCKED(int networkId) {
+ WifiNetworkCollection::iterator it;
+ for (it = mNetworks->begin(); it != mNetworks->end(); ++it) {
+ if ((*it)->getNetworkId() == networkId) {
+ return *it;
+ }
+ }
+ errno = ENOENT;
+ return NULL;
+}
+
+WifiNetworkCollection *Supplicant::createNetworkList() {
+ WifiNetworkCollection *d = new WifiNetworkCollection();
+ WifiNetworkCollection::iterator i;
+
+ pthread_mutex_lock(&mNetworksLock);
+ for (i = mNetworks->begin(); i != mNetworks->end(); ++i)
+ d->push_back((*i)->clone());
+
+ pthread_mutex_unlock(&mNetworksLock);
+ return d;
+}
+
+int Supplicant::setupConfig() {
+ char buf[2048];
+ int srcfd, destfd;
+ int nread;
+
+ if (access(SUPP_CONFIG_FILE, R_OK|W_OK) == 0) {
+ return 0;
+ } else if (errno != ENOENT) {
+ LOGE("Cannot access \"%s\": %s", SUPP_CONFIG_FILE, strerror(errno));
+ return -1;
+ }
+
+ srcfd = open(SUPP_CONFIG_TEMPLATE, O_RDONLY);
+ if (srcfd < 0) {
+ LOGE("Cannot open \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno));
+ return -1;
+ }
+
+ destfd = open(SUPP_CONFIG_FILE, O_CREAT|O_WRONLY, 0660);
+ if (destfd < 0) {
+ close(srcfd);
+ LOGE("Cannot create \"%s\": %s", SUPP_CONFIG_FILE, strerror(errno));
+ return -1;
+ }
+
+ while ((nread = read(srcfd, buf, sizeof(buf))) != 0) {
+ if (nread < 0) {
+ LOGE("Error reading \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno));
+ close(srcfd);
+ close(destfd);
+ unlink(SUPP_CONFIG_FILE);
+ return -1;
+ }
+ write(destfd, buf, nread);
+ }
+
+ close(destfd);
+ close(srcfd);
+
+ if (chown(SUPP_CONFIG_FILE, AID_SYSTEM, AID_WIFI) < 0) {
+ LOGE("Error changing group ownership of %s to %d: %s",
+ SUPP_CONFIG_FILE, AID_WIFI, strerror(errno));
+ unlink(SUPP_CONFIG_FILE);
+ return -1;
+ }
+ return 0;
+}
+
+int Supplicant::setNetworkVar(int networkId, const char *var, const char *val) {
+ char reply[255];
+ size_t len = sizeof(reply) -1;
+
+ LOGD("netid %d, var '%s' = '%s'", networkId, var, val);
+ char *tmp;
+ asprintf(&tmp, "SET_NETWORK %d %s %s", networkId, var, val);
+ if (sendCommand(tmp, reply, &len)) {
+ free(tmp);
+ return -1;
+ }
+ free(tmp);
+
+ len = sizeof(reply) -1;
+ if (sendCommand("SAVE_CONFIG", reply, &len)) {
+ LOGE("Error saving config after %s = %s", var, val);
+ return -1;
+ }
+ return 0;
+}
+
+const char *Supplicant::getNetworkVar(int networkId, const char *var,
+ char *buffer, size_t max) {
+ size_t len = max - 1;
+ char *tmp;
+
+ asprintf(&tmp, "GET_NETWORK %d %s", networkId, var);
+ if (sendCommand(tmp, buffer, &len)) {
+ free(tmp);
+ return NULL;
+ }
+ free(tmp);
+ return buffer;
+}
+
+int Supplicant::enableNetwork(int networkId, bool enabled) {
+ char req[64];
+
+ if (enabled)
+ sprintf(req, "ENABLE_NETWORK %d", networkId);
+ else
+ sprintf(req, "DISABLE_NETWORK %d", networkId);
+
+ char reply[16];
+ size_t len = sizeof(reply) -1;
+
+ if (sendCommand(req, reply, &len))
+ return -1;
+ return 0;
+}
+
+int Supplicant::enablePacketFilter() {
+ char req[128];
+ char reply[16];
+ size_t len;
+ int i;
+
+ for (i = 0; i <=3; i++) {
+ snprintf(req, sizeof(req), "DRIVER RXFILTER-ADD %d", i);
+ len = sizeof(reply);
+ if (sendCommand(req, reply, &len))
+ return -1;
+ }
+
+ len = sizeof(reply);
+ if (sendCommand("DRIVER RXFILTER-START", reply, &len))
+ return -1;
+ return 0;
+}
+
+int Supplicant::disablePacketFilter() {
+ char req[128];
+ char reply[16];
+ size_t len;
+ int i;
+
+ len = sizeof(reply);
+ if (sendCommand("DRIVER RXFILTER-STOP", reply, &len))
+ return -1;
+
+ for (i = 3; i >=0; i--) {
+ snprintf(req, sizeof(req), "DRIVER RXFILTER-REMOVE %d", i);
+ len = sizeof(reply);
+ if (sendCommand(req, reply, &len))
+ return -1;
+ }
+ return 0;
+}
+
+int Supplicant::enableBluetoothCoexistenceScan() {
+ char req[128];
+ char reply[16];
+ size_t len;
+ int i;
+
+ len = sizeof(reply);
+ if (sendCommand("DRIVER BTCOEXSCAN-START", reply, &len))
+ return -1;
+ return 0;
+}
+
+int Supplicant::disableBluetoothCoexistenceScan() {
+ char req[128];
+ char reply[16];
+ size_t len;
+ int i;
+
+ len = sizeof(reply);
+ if (sendCommand("DRIVER BTCOEXSCAN-STOP", reply, &len))
+ return -1;
+ return 0;
+}
+
+int Supplicant::setBluetoothCoexistenceMode(int mode) {
+ char req[64];
+
+ sprintf(req, "DRIVER BTCOEXMODE %d", mode);
+
+ char reply[16];
+ size_t len = sizeof(reply) -1;
+
+ if (sendCommand(req, reply, &len))
+ return -1;
+ return 0;
+}
+
+int Supplicant::setApScanMode(int mode) {
+ char req[64];
+
+// LOGD("setApScanMode(%d)", mode);
+ sprintf(req, "AP_SCAN %d", mode);
+
+ char reply[16];
+ size_t len = sizeof(reply) -1;
+
+ if (sendCommand(req, reply, &len))
+ return -1;
+ return 0;
+}
+
+
+int Supplicant::retrieveInterfaceName() {
+ char reply[255];
+ size_t len = sizeof(reply) -1;
+
+ if (sendCommand("INTERFACES", reply, &len))
+ return -1;
+
+ reply[strlen(reply)-1] = '\0';
+ mInterfaceName = strdup(reply);
+ return 0;
+}
+
+int Supplicant::reconnect() {
+ char req[128];
+ char reply[16];
+ size_t len;
+ int i;
+
+ len = sizeof(reply);
+ if (sendCommand("RECONNECT", reply, &len))
+ return -1;
+ return 0;
+}
+
+int Supplicant::disconnect() {
+ char req[128];
+ char reply[16];
+ size_t len;
+ int i;
+
+ len = sizeof(reply);
+ if (sendCommand("DISCONNECT", reply, &len))
+ return -1;
+ return 0;
+}
+
+int Supplicant::getNetworkCount() {
+ pthread_mutex_lock(&mNetworksLock);
+ int cnt = mNetworks->size();
+ pthread_mutex_unlock(&mNetworksLock);
+ return cnt;
+}