blob: e930c5c37638f09346abcffc0a6ab041d8fb5bc2 [file] [log] [blame]
/*
* Copyright 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.
*/
#define LOG_TAG "wifi"
#include "jni.h"
#include <ScopedUtfChars.h>
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
#include <utils/String16.h>
#include "wifi.h"
#define WIFI_PKG_NAME "android/net/wifi/WifiNative"
#define BUF_SIZE 256
namespace android {
static jboolean sScanModeActive = false;
static int doCommand(const char *cmd, char *replybuf, int replybuflen)
{
size_t reply_len = replybuflen - 1;
if (::wifi_command(cmd, replybuf, &reply_len) != 0)
return -1;
else {
// Strip off trailing newline
if (reply_len > 0 && replybuf[reply_len-1] == '\n')
replybuf[reply_len-1] = '\0';
else
replybuf[reply_len] = '\0';
return 0;
}
}
static jint doIntCommand(const char* fmt, ...)
{
char buf[BUF_SIZE];
va_list args;
va_start(args, fmt);
int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
if (byteCount < 0 || byteCount >= BUF_SIZE) {
return -1;
}
char reply[BUF_SIZE];
if (doCommand(buf, reply, sizeof(reply)) != 0) {
return -1;
}
return static_cast<jint>(atoi(reply));
}
static jboolean doBooleanCommand(const char* expect, const char* fmt, ...)
{
char buf[BUF_SIZE];
va_list args;
va_start(args, fmt);
int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
if (byteCount < 0 || byteCount >= BUF_SIZE) {
return JNI_FALSE;
}
char reply[BUF_SIZE];
if (doCommand(buf, reply, sizeof(reply)) != 0) {
return JNI_FALSE;
}
return (strcmp(reply, expect) == 0);
}
// Send a command to the supplicant, and return the reply as a String
static jstring doStringCommand(JNIEnv* env, const char* fmt, ...) {
char buf[BUF_SIZE];
va_list args;
va_start(args, fmt);
int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
if (byteCount < 0 || byteCount >= BUF_SIZE) {
return NULL;
}
char reply[4096];
if (doCommand(buf, reply, sizeof(reply)) != 0) {
return NULL;
}
// TODO: why not just NewStringUTF?
String16 str((char *)reply);
return env->NewString((const jchar *)str.string(), str.size());
}
static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jobject)
{
return (jboolean)(::is_wifi_driver_loaded() == 1);
}
static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject)
{
return (jboolean)(::wifi_load_driver() == 0);
}
static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jobject)
{
return (jboolean)(::wifi_unload_driver() == 0);
}
static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject)
{
return (jboolean)(::wifi_start_supplicant() == 0);
}
static jboolean android_net_wifi_stopSupplicant(JNIEnv* env, jobject)
{
return doBooleanCommand("OK", "TERMINATE");
}
static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject)
{
return (jboolean)(::wifi_stop_supplicant() == 0);
}
static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject)
{
return (jboolean)(::wifi_connect_to_supplicant() == 0);
}
static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject)
{
::wifi_close_supplicant_connection();
}
static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject)
{
char buf[BUF_SIZE];
int nread = ::wifi_wait_for_event(buf, sizeof buf);
if (nread > 0) {
return env->NewStringUTF(buf);
} else {
return NULL;
}
}
static jstring android_net_wifi_listNetworksCommand(JNIEnv* env, jobject)
{
return doStringCommand(env, "LIST_NETWORKS");
}
static jint android_net_wifi_addNetworkCommand(JNIEnv* env, jobject)
{
return doIntCommand("ADD_NETWORK");
}
static jboolean android_net_wifi_wpsPbcCommand(JNIEnv* env, jobject, jstring javaBssid)
{
ScopedUtfChars bssid(env, javaBssid);
if (bssid.c_str() == NULL) {
return JNI_FALSE;
}
return doBooleanCommand("OK", "WPS_PBC %s", bssid.c_str());
}
static jboolean android_net_wifi_wpsPinFromAccessPointCommand(JNIEnv* env, jobject,
jstring javaBssid, jstring javaApPin)
{
ScopedUtfChars bssid(env, javaBssid);
if (bssid.c_str() == NULL) {
return JNI_FALSE;
}
ScopedUtfChars apPin(env, javaApPin);
if (apPin.c_str() == NULL) {
return JNI_FALSE;
}
return doBooleanCommand("OK", "WPS_REG %s %s", bssid.c_str(), apPin.c_str());
}
static jstring android_net_wifi_wpsPinFromDeviceCommand(JNIEnv* env, jobject, jstring javaBssid)
{
ScopedUtfChars bssid(env, javaBssid);
if (bssid.c_str() == NULL) {
return NULL;
}
return doStringCommand(env, "WPS_PIN %s", bssid.c_str());
}
static jboolean android_net_wifi_setCountryCodeCommand(JNIEnv* env, jobject, jstring javaCountry)
{
ScopedUtfChars country(env, javaCountry);
if (country.c_str() == NULL) {
return JNI_FALSE;
}
return doBooleanCommand("OK", "DRIVER COUNTRY %s", country.c_str());
}
static jboolean android_net_wifi_setNetworkVariableCommand(JNIEnv* env,
jobject,
jint netId,
jstring javaName,
jstring javaValue)
{
ScopedUtfChars name(env, javaName);
if (name.c_str() == NULL) {
return JNI_FALSE;
}
ScopedUtfChars value(env, javaValue);
if (value.c_str() == NULL) {
return JNI_FALSE;
}
return doBooleanCommand("OK", "SET_NETWORK %d %s %s", netId, name.c_str(), value.c_str());
}
static jstring android_net_wifi_getNetworkVariableCommand(JNIEnv* env,
jobject,
jint netId,
jstring javaName)
{
ScopedUtfChars name(env, javaName);
if (name.c_str() == NULL) {
return NULL;
}
return doStringCommand(env, "GET_NETWORK %d %s", netId, name.c_str());
}
static jboolean android_net_wifi_removeNetworkCommand(JNIEnv* env, jobject, jint netId)
{
return doBooleanCommand("OK", "REMOVE_NETWORK %d", netId);
}
static jboolean android_net_wifi_enableNetworkCommand(JNIEnv* env,
jobject,
jint netId,
jboolean disableOthers)
{
return doBooleanCommand("OK", "%s_NETWORK %d", disableOthers ? "SELECT" : "ENABLE", netId);
}
static jboolean android_net_wifi_disableNetworkCommand(JNIEnv* env, jobject, jint netId)
{
return doBooleanCommand("OK", "DISABLE_NETWORK %d", netId);
}
static jstring android_net_wifi_statusCommand(JNIEnv* env, jobject)
{
return doStringCommand(env, "STATUS");
}
static jboolean android_net_wifi_pingCommand(JNIEnv* env, jobject)
{
return doBooleanCommand("PONG", "PING");
}
static jstring android_net_wifi_scanResultsCommand(JNIEnv* env, jobject)
{
return doStringCommand(env, "SCAN_RESULTS");
}
static jboolean android_net_wifi_disconnectCommand(JNIEnv* env, jobject)
{
return doBooleanCommand("OK", "DISCONNECT");
}
static jboolean android_net_wifi_reconnectCommand(JNIEnv* env, jobject)
{
return doBooleanCommand("OK", "RECONNECT");
}
static jboolean android_net_wifi_reassociateCommand(JNIEnv* env, jobject)
{
return doBooleanCommand("OK", "REASSOCIATE");
}
static jboolean doSetScanMode(jboolean setActive)
{
return doBooleanCommand("OK", (setActive ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"));
}
static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject, jboolean forceActive)
{
jboolean result;
// Ignore any error from setting the scan mode.
// The scan will still work.
if (forceActive && !sScanModeActive)
doSetScanMode(true);
result = doBooleanCommand("OK", "SCAN");
if (forceActive && !sScanModeActive)
doSetScanMode(sScanModeActive);
return result;
}
static jboolean android_net_wifi_setScanModeCommand(JNIEnv* env, jobject, jboolean setActive)
{
sScanModeActive = setActive;
return doSetScanMode(setActive);
}
static jboolean android_net_wifi_startDriverCommand(JNIEnv* env, jobject)
{
return doBooleanCommand("OK", "DRIVER START");
}
static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject)
{
return doBooleanCommand("OK", "DRIVER STOP");
}
static jboolean android_net_wifi_startPacketFiltering(JNIEnv* env, jobject)
{
return doBooleanCommand("OK", "DRIVER RXFILTER-ADD 0")
&& doBooleanCommand("OK", "DRIVER RXFILTER-ADD 1")
&& doBooleanCommand("OK", "DRIVER RXFILTER-ADD 3")
&& doBooleanCommand("OK", "DRIVER RXFILTER-START");
}
static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject)
{
jboolean result = doBooleanCommand("OK", "DRIVER RXFILTER-STOP");
if (result) {
(void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 3");
(void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 1");
(void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 0");
}
return result;
}
static jint android_net_wifi_getRssiHelper(const char *cmd)
{
char reply[BUF_SIZE];
int rssi = -200;
if (doCommand(cmd, reply, sizeof(reply)) != 0) {
return (jint)-1;
}
// reply comes back in the form "<SSID> rssi XX" where XX is the
// number we're interested in. if we're associating, it returns "OK".
// beware - <SSID> can contain spaces.
if (strcmp(reply, "OK") != 0) {
// beware of trailing spaces
char* end = reply + strlen(reply);
while (end > reply && end[-1] == ' ') {
end--;
}
*end = 0;
char* lastSpace = strrchr(reply, ' ');
// lastSpace should be preceded by "rssi" and followed by the value
if (lastSpace && !strncasecmp(lastSpace - 4, "rssi", 4)) {
sscanf(lastSpace + 1, "%d", &rssi);
}
}
return (jint)rssi;
}
static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject)
{
return android_net_wifi_getRssiHelper("DRIVER RSSI");
}
static jint android_net_wifi_getRssiApproxCommand(JNIEnv* env, jobject)
{
return android_net_wifi_getRssiHelper("DRIVER RSSI-APPROX");
}
static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject)
{
char reply[BUF_SIZE];
int linkspeed;
if (doCommand("DRIVER LINKSPEED", reply, sizeof(reply)) != 0) {
return (jint)-1;
}
// reply comes back in the form "LinkSpeed XX" where XX is the
// number we're interested in.
sscanf(reply, "%*s %u", &linkspeed);
return (jint)linkspeed;
}
static jstring android_net_wifi_getMacAddressCommand(JNIEnv* env, jobject)
{
char reply[BUF_SIZE];
char buf[BUF_SIZE];
if (doCommand("DRIVER MACADDR", reply, sizeof(reply)) != 0) {
return NULL;
}
// reply comes back in the form "Macaddr = XX.XX.XX.XX.XX.XX" where XX
// is the part of the string we're interested in.
if (sscanf(reply, "%*s = %255s", buf) == 1) {
return env->NewStringUTF(buf);
}
return NULL;
}
static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject, jint mode)
{
return doBooleanCommand("OK", "DRIVER POWERMODE %d", mode);
}
static jint android_net_wifi_getPowerModeCommand(JNIEnv* env, jobject)
{
char reply[BUF_SIZE];
int power;
if (doCommand("DRIVER GETPOWER", reply, sizeof(reply)) != 0) {
return (jint)-1;
}
// reply comes back in the form "powermode = XX" where XX is the
// number we're interested in.
if (sscanf(reply, "%*s = %u", &power) != 1) {
return (jint)-1;
}
return (jint)power;
}
static jboolean android_net_wifi_setBandCommand(JNIEnv* env, jobject, jint band)
{
return doBooleanCommand("OK", "DRIVER SETBAND %d", band);
}
static jint android_net_wifi_getBandCommand(JNIEnv* env, jobject)
{
char reply[25];
int band;
if (doCommand("DRIVER GETBAND", reply, sizeof(reply)) != 0) {
return (jint)-1;
}
// reply comes back in the form "Band X" where X is the
// number we're interested in.
sscanf(reply, "%*s %u", &band);
return (jint)band;
}
static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, jobject, jint mode)
{
return doBooleanCommand("OK", "DRIVER BTCOEXMODE %d", mode);
}
static jboolean android_net_wifi_setBluetoothCoexistenceScanModeCommand(JNIEnv* env, jobject, jboolean setCoexScanMode)
{
return doBooleanCommand("OK", "DRIVER BTCOEXSCAN-%s", setCoexScanMode ? "START" : "STOP");
}
static jboolean android_net_wifi_saveConfigCommand(JNIEnv* env, jobject)
{
// Make sure we never write out a value for AP_SCAN other than 1
(void)doBooleanCommand("OK", "AP_SCAN 1");
return doBooleanCommand("OK", "SAVE_CONFIG");
}
static jboolean android_net_wifi_reloadConfigCommand(JNIEnv* env, jobject)
{
return doBooleanCommand("OK", "RECONFIGURE");
}
static jboolean android_net_wifi_setScanResultHandlingCommand(JNIEnv* env, jobject, jint mode)
{
return doBooleanCommand("OK", "AP_SCAN %d", mode);
}
static jboolean android_net_wifi_addToBlacklistCommand(JNIEnv* env, jobject, jstring javaBssid)
{
ScopedUtfChars bssid(env, javaBssid);
if (bssid.c_str() == NULL) {
return JNI_FALSE;
}
return doBooleanCommand("OK", "BLACKLIST %s", bssid.c_str());
}
static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject)
{
return doBooleanCommand("OK", "BLACKLIST clear");
}
static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject, jboolean enabled)
{
return doBooleanCommand("OK", "DRIVER SETSUSPENDOPT %d", enabled ? 0 : 1);
}
static void android_net_wifi_enableBackgroundScanCommand(JNIEnv* env, jobject, jboolean enable)
{
//Note: BGSCAN-START and BGSCAN-STOP are documented in core/res/res/values/config.xml
//and will need an update if the names are changed
if (enable) {
doBooleanCommand("OK", "DRIVER BGSCAN-START");
} else {
doBooleanCommand("OK", "DRIVER BGSCAN-STOP");
}
}
static void android_net_wifi_setScanIntervalCommand(JNIEnv* env, jobject, jint scanInterval)
{
doBooleanCommand("OK", "SCAN_INTERVAL %d", scanInterval);
}
// ----------------------------------------------------------------------------
/*
* JNI registration.
*/
static JNINativeMethod gWifiMethods[] = {
/* name, signature, funcPtr */
{ "loadDriver", "()Z", (void *)android_net_wifi_loadDriver },
{ "isDriverLoaded", "()Z", (void *)android_net_wifi_isDriverLoaded},
{ "unloadDriver", "()Z", (void *)android_net_wifi_unloadDriver },
{ "startSupplicant", "()Z", (void *)android_net_wifi_startSupplicant },
{ "stopSupplicant", "()Z", (void*) android_net_wifi_stopSupplicant },
{ "killSupplicant", "()Z", (void *)android_net_wifi_killSupplicant },
{ "connectToSupplicant", "()Z", (void *)android_net_wifi_connectToSupplicant },
{ "closeSupplicantConnection", "()V", (void *)android_net_wifi_closeSupplicantConnection },
{ "listNetworksCommand", "()Ljava/lang/String;",
(void*) android_net_wifi_listNetworksCommand },
{ "addNetworkCommand", "()I", (void*) android_net_wifi_addNetworkCommand },
{ "setNetworkVariableCommand", "(ILjava/lang/String;Ljava/lang/String;)Z",
(void*) android_net_wifi_setNetworkVariableCommand },
{ "getNetworkVariableCommand", "(ILjava/lang/String;)Ljava/lang/String;",
(void*) android_net_wifi_getNetworkVariableCommand },
{ "removeNetworkCommand", "(I)Z", (void*) android_net_wifi_removeNetworkCommand },
{ "enableNetworkCommand", "(IZ)Z", (void*) android_net_wifi_enableNetworkCommand },
{ "disableNetworkCommand", "(I)Z", (void*) android_net_wifi_disableNetworkCommand },
{ "waitForEvent", "()Ljava/lang/String;", (void*) android_net_wifi_waitForEvent },
{ "statusCommand", "()Ljava/lang/String;", (void*) android_net_wifi_statusCommand },
{ "scanResultsCommand", "()Ljava/lang/String;", (void*) android_net_wifi_scanResultsCommand },
{ "pingCommand", "()Z", (void *)android_net_wifi_pingCommand },
{ "disconnectCommand", "()Z", (void *)android_net_wifi_disconnectCommand },
{ "reconnectCommand", "()Z", (void *)android_net_wifi_reconnectCommand },
{ "reassociateCommand", "()Z", (void *)android_net_wifi_reassociateCommand },
{ "scanCommand", "(Z)Z", (void*) android_net_wifi_scanCommand },
{ "setScanModeCommand", "(Z)Z", (void*) android_net_wifi_setScanModeCommand },
{ "startDriverCommand", "()Z", (void*) android_net_wifi_startDriverCommand },
{ "stopDriverCommand", "()Z", (void*) android_net_wifi_stopDriverCommand },
{ "startPacketFiltering", "()Z", (void*) android_net_wifi_startPacketFiltering },
{ "stopPacketFiltering", "()Z", (void*) android_net_wifi_stopPacketFiltering },
{ "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },
{ "getPowerModeCommand", "()I", (void*) android_net_wifi_getPowerModeCommand },
{ "setBandCommand", "(I)Z", (void*) android_net_wifi_setBandCommand},
{ "getBandCommand", "()I", (void*) android_net_wifi_getBandCommand},
{ "setBluetoothCoexistenceModeCommand", "(I)Z",
(void*) android_net_wifi_setBluetoothCoexistenceModeCommand },
{ "setBluetoothCoexistenceScanModeCommand", "(Z)Z",
(void*) android_net_wifi_setBluetoothCoexistenceScanModeCommand },
{ "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand },
{ "getRssiApproxCommand", "()I",
(void*) android_net_wifi_getRssiApproxCommand},
{ "getLinkSpeedCommand", "()I", (void*) android_net_wifi_getLinkSpeedCommand },
{ "getMacAddressCommand", "()Ljava/lang/String;", (void*) android_net_wifi_getMacAddressCommand },
{ "saveConfigCommand", "()Z", (void*) android_net_wifi_saveConfigCommand },
{ "reloadConfigCommand", "()Z", (void*) android_net_wifi_reloadConfigCommand },
{ "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand },
{ "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand },
{ "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand },
{ "startWpsPbcCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_wpsPbcCommand },
{ "startWpsWithPinFromAccessPointCommand", "(Ljava/lang/String;Ljava/lang/String;)Z",
(void*) android_net_wifi_wpsPinFromAccessPointCommand },
{ "startWpsWithPinFromDeviceCommand", "(Ljava/lang/String;)Ljava/lang/String;",
(void*) android_net_wifi_wpsPinFromDeviceCommand },
{ "setSuspendOptimizationsCommand", "(Z)Z",
(void*) android_net_wifi_setSuspendOptimizationsCommand},
{ "setCountryCodeCommand", "(Ljava/lang/String;)Z",
(void*) android_net_wifi_setCountryCodeCommand},
{ "enableBackgroundScanCommand", "(Z)V", (void*) android_net_wifi_enableBackgroundScanCommand},
{ "setScanIntervalCommand", "(I)V", (void*) android_net_wifi_setScanIntervalCommand},
};
int register_android_net_wifi_WifiManager(JNIEnv* env)
{
return AndroidRuntime::registerNativeMethods(env,
WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));
}
}; // namespace android