diff options
Diffstat (limited to 'nexus/Supplicant.cpp')
-rw-r--r-- | nexus/Supplicant.cpp | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/nexus/Supplicant.cpp b/nexus/Supplicant.cpp new file mode 100644 index 0000000..215a8b3 --- /dev/null +++ b/nexus/Supplicant.cpp @@ -0,0 +1,377 @@ +/* + * 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 <errno.h> + +#define LOG_TAG "Supplicant" +#include <cutils/log.h> +#include <cutils/properties.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 "Supplicant.h" +#include "SupplicantListener.h" +#include "SupplicantState.h" +#include "SupplicantEvent.h" +#include "ScanResult.h" + +#include "libwpa_client/wpa_ctrl.h" + +#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" + +Supplicant::Supplicant() { + mCtrl = NULL; + mMonitor = NULL; + mListener = NULL; + + mState = SupplicantState::UNKNOWN; + + mLatestScanResults = new ScanResultCollection(); + + pthread_mutex_init(&mLatestScanResultsLock, NULL); +} + +int Supplicant::start() { + LOGD("start():"); + // XXX: Validate supplicant config file + + 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") == 0) { + return 0; + } + + wpa_ctrl_cleanup(); +#ifdef HAVE_LIBC_SYSTEM_PROPERTIES + pi = __system_property_find(SUPP_PROP_NAME); + if (pi != NULL) + serial = pi->serial; +#endif + + 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) + return 0; + 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") == 0) + break; + } +#endif + usleep(100000); + } + + if (!count) { + errno = ETIMEDOUT; + return -1; + } + + if (connectToSupplicant()) { + LOGE("Error connecting to supplicant (%s)\n", strerror(errno)); + return -1; + } + return 0; +} + +int Supplicant::stop() { + LOGD("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) { + return 0; + } + + 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 (mCtrl) { + wpa_ctrl_close(mCtrl); + mCtrl = NULL; + } + if (mMonitor) { + wpa_ctrl_close(mMonitor); + mMonitor = NULL; + } + + if (!count) { + LOGD("Timed out waiting for supplicant to stop"); + errno = ETIMEDOUT; + return -1; + } + + LOGD("Stopped OK"); + + return 0; +} + +bool Supplicant::isStarted() { + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + if (!property_get(SUPP_PROP_NAME, supp_status, NULL) || + !strcmp(supp_status, "running")) { + return false; + } + return true; +} + +int Supplicant::connectToSupplicant() { + char ifname[256]; + char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; + + if (!property_get(SUPP_PROP_NAME, supp_status, NULL) + || strcmp(supp_status, "running") != 0) { + LOGE("Supplicant not running, cannot connect"); + return -1; + } + + mCtrl = wpa_ctrl_open("tiwlan0"); + 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(this, 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; + 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)) { + errno = EIO; + return -1; + } + + if (!strncmp(cmd, "PING", 4) || + !strncmp(cmd, "SCAN_RESULTS", 12)) + reply[*reply_len] = '\0'; + + 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; +} + +int Supplicant::onConnectedEvent(SupplicantEvent *evt) { + LOGD("onConnectedEvent(%s)", evt->getEvent()); + return 0; +} + +int Supplicant::onDisconnectedEvent(SupplicantEvent *evt) { + LOGD("onDisconnectedEvent(%s)", evt->getEvent()); + return 0; +} + +int Supplicant::onTerminatingEvent(SupplicantEvent *evt) { + LOGD("onTerminatingEvent(%s)", evt->getEvent()); + return 0; +} + +int Supplicant::onPasswordChangedEvent(SupplicantEvent *evt) { + LOGD("onPasswordChangedEvent(%s)", evt->getEvent()); + return 0; +} + +int Supplicant::onEapNotificationEvent(SupplicantEvent *evt) { + LOGD("onEapNotificationEvent(%s)", evt->getEvent()); + return 0; +} + +int Supplicant::onEapStartedEvent(SupplicantEvent *evt) { + LOGD("onEapStartedEvent(%s)", evt->getEvent()); + return 0; +} + +int Supplicant::onEapMethodEvent(SupplicantEvent *evt) { + LOGD("onEapMethodEvent(%s)", evt->getEvent()); + return 0; +} + +int Supplicant::onEapSuccessEvent(SupplicantEvent *evt) { + LOGD("onEapSuccessEvent(%s)", evt->getEvent()); + return 0; +} + +int Supplicant::onEapFailureEvent(SupplicantEvent *evt) { + LOGD("onEapFailureEvent(%s)", evt->getEvent()); + return 0; +} + +int Supplicant::onScanResultsEvent(SupplicantEvent *evt) { + LOGD("onScanResultsEvent(%s)", evt->getEvent()); + + if (!strcmp(evt->getEvent(), "Ready")) { + char *reply; + + if (!(reply = (char *) malloc(4096))) { + errno = -ENOMEM; + return -1; + } + + size_t len = 4096; + + if (sendCommand("SCAN_RESULTS", reply, &len)) { + LOGW("onScanResultsEvent(%s): Error getting scan results (%s)", + evt->getEvent(), strerror(errno)); + free(reply); + return -1; + } + + pthread_mutex_lock(&mLatestScanResultsLock); + if (!mLatestScanResults->empty()) { + ScanResultCollection::iterator i; + + for (i = mLatestScanResults->begin(); + i !=mLatestScanResults->end(); ++i) { + delete *i; + } + mLatestScanResults->clear(); + } + + char *linep; + char *linep_next = NULL; + + if (!strtok_r(reply, "\n", &linep_next)) { + free(reply); + return 0;; + } + + while((linep = strtok_r(NULL, "\n", &linep_next))) + mLatestScanResults->push_back(new ScanResult(linep)); + + pthread_mutex_unlock(&mLatestScanResultsLock); + free(reply); + } else { + LOGW("Unknown SCAN_RESULTS event (%s)", evt->getEvent()); + } + return 0; +} + +int Supplicant::onStateChangeEvent(SupplicantEvent *evt) { + LOGD("onStateChangeEvent(%s)", evt->getEvent()); + // XXX: Update mState + return 0; +} + +int Supplicant::onLinkSpeedEvent(SupplicantEvent *evt) { + LOGD("onLinkSpeedEvent(%s)", evt->getEvent()); + return 0; +} + +int Supplicant::onDriverStateEvent(SupplicantEvent *evt) { + LOGD("onDriverStateEvent(%s)", evt->getEvent()); + return 0; +} + +// XXX: Use a cursor + smartptr instead +const ScanResultCollection *Supplicant::getLatestScanResults() { + ScanResultCollection *d = new ScanResultCollection(); + ScanResultCollection::iterator i; + + pthread_mutex_lock(&mLatestScanResultsLock); + for (i = mLatestScanResults->begin(); i != mLatestScanResults->end(); ++i) { + d->push_back((*i)->clone()); + } + + pthread_mutex_unlock(&mLatestScanResultsLock); + return d; +}; |