/* * 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 "ErrorCode.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); } 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; } 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++; new_wn->registerProperties(); 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++; (*i)->unregisterProperties(); 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::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; } int Supplicant::triggerScan(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; } len = sizeof(reply); if (sendCommand("SCAN", reply, &len)) { LOGW("triggerScan(%d): Error initiating scan", active); 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)); pthread_mutex_lock(&mNetworksLock); 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; 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::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; }