gecko/dom/system/gonk/NetworkUtils.cpp

1411 lines
43 KiB
C++

/* Copyright 2012 Mozilla Foundation and Mozilla contributors
*
* 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 "NetworkUtils.h"
#include <android/log.h>
#include <cutils/properties.h>
#include <limits>
#include "mozilla/dom/network/NetUtils.h"
#define _DEBUG 0
#define WARN(args...) __android_log_print(ANDROID_LOG_WARN, "NetworlUtils", ## args)
#define ERROR(args...) __android_log_print(ANDROID_LOG_ERROR, "NetworkUtils", ## args)
#if _DEBUG
#define DEBUG(args...) __android_log_print(ANDROID_LOG_DEBUG, "NetworkUtils" , ## args)
#else
#define DEBUG(args...)
#endif
using namespace mozilla::dom;
using namespace mozilla::ipc;
static const char* PERSIST_SYS_USB_CONFIG_PROPERTY = "persist.sys.usb.config";
static const char* SYS_USB_CONFIG_PROPERTY = "sys.usb.config";
static const char* SYS_USB_STATE_PROPERTY = "sys.usb.state";
static const char* USB_FUNCTION_RNDIS = "rndis";
static const char* USB_FUNCTION_ADB = "adb";
// Use this command to continue the function chain.
static const char* DUMMY_COMMAND = "tether status";
// Retry 20 times (2 seconds) for usb state transition.
static const uint32_t USB_FUNCTION_RETRY_TIMES = 20;
// Check "sys.usb.state" every 100ms.
static const uint32_t USB_FUNCTION_RETRY_INTERVAL = 100;
// 1xx - Requested action is proceeding
static const uint32_t NETD_COMMAND_PROCEEDING = 100;
// 2xx - Requested action has been successfully completed
static const uint32_t NETD_COMMAND_OKAY = 200;
// 4xx - The command is accepted but the requested action didn't
// take place.
static const uint32_t NETD_COMMAND_FAIL = 400;
// 5xx - The command syntax or parameters error
static const uint32_t NETD_COMMAND_ERROR = 500;
// 6xx - Unsolicited broadcasts
static const uint32_t NETD_COMMAND_UNSOLICITED = 600;
// Broadcast messages
static const uint32_t NETD_COMMAND_INTERFACE_CHANGE = 600;
static const uint32_t NETD_COMMAND_BANDWIDTH_CONTROLLER = 601;
static const char* INTERFACE_DELIMIT = "\0";
static const char* USB_CONFIG_DELIMIT = ",";
static const char* NETD_MESSAGE_DELIMIT = " ";
static const uint32_t BUF_SIZE = 1024;
static uint32_t SDK_VERSION;
struct IFProperties {
char gateway[PROPERTY_VALUE_MAX];
char dns1[PROPERTY_VALUE_MAX];
char dns2[PROPERTY_VALUE_MAX];
};
struct CurrentCommand {
CommandChain* chain;
CommandCallback callback;
char command[MAX_COMMAND_SIZE];
};
typedef Tuple3<NetdCommand*, CommandChain*, CommandCallback> QueueData;
#define GET_CURRENT_NETD_COMMAND (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].a)
#define GET_CURRENT_CHAIN (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].b)
#define GET_CURRENT_CALLBACK (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].c)
#define GET_CURRENT_COMMAND (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].a->mData)
static NetworkUtils* gNetworkUtils;
static nsTArray<QueueData> gCommandQueue;
static CurrentCommand gCurrentCommand;
static bool gPending = false;
static nsTArray<nsCString> gReason;
CommandFunc NetworkUtils::sWifiEnableChain[] = {
NetworkUtils::wifiFirmwareReload,
NetworkUtils::startAccessPointDriver,
NetworkUtils::setAccessPoint,
NetworkUtils::startSoftAP,
NetworkUtils::setInterfaceUp,
NetworkUtils::tetherInterface,
NetworkUtils::setIpForwardingEnabled,
NetworkUtils::tetheringStatus,
NetworkUtils::startTethering,
NetworkUtils::setDnsForwarders,
NetworkUtils::enableNat,
NetworkUtils::wifiTetheringSuccess
};
CommandFunc NetworkUtils::sWifiDisableChain[] = {
NetworkUtils::stopSoftAP,
NetworkUtils::stopAccessPointDriver,
NetworkUtils::wifiFirmwareReload,
NetworkUtils::untetherInterface,
NetworkUtils::preTetherInterfaceList,
NetworkUtils::postTetherInterfaceList,
NetworkUtils::disableNat,
NetworkUtils::setIpForwardingEnabled,
NetworkUtils::stopTethering,
NetworkUtils::wifiTetheringSuccess
};
CommandFunc NetworkUtils::sWifiFailChain[] = {
NetworkUtils::stopSoftAP,
NetworkUtils::setIpForwardingEnabled,
NetworkUtils::stopTethering
};
CommandFunc NetworkUtils::sWifiOperationModeChain[] = {
NetworkUtils::wifiFirmwareReload,
NetworkUtils::wifiOperationModeSuccess
};
CommandFunc NetworkUtils::sUSBEnableChain[] = {
NetworkUtils::setInterfaceUp,
NetworkUtils::enableNat,
NetworkUtils::setIpForwardingEnabled,
NetworkUtils::tetherInterface,
NetworkUtils::tetheringStatus,
NetworkUtils::startTethering,
NetworkUtils::setDnsForwarders,
NetworkUtils::usbTetheringSuccess
};
CommandFunc NetworkUtils::sUSBDisableChain[] = {
NetworkUtils::untetherInterface,
NetworkUtils::preTetherInterfaceList,
NetworkUtils::postTetherInterfaceList,
NetworkUtils::disableNat,
NetworkUtils::setIpForwardingEnabled,
NetworkUtils::stopTethering,
NetworkUtils::usbTetheringSuccess
};
CommandFunc NetworkUtils::sUSBFailChain[] = {
NetworkUtils::stopSoftAP,
NetworkUtils::setIpForwardingEnabled,
NetworkUtils::stopTethering
};
CommandFunc NetworkUtils::sUpdateUpStreamChain[] = {
NetworkUtils::cleanUpStream,
NetworkUtils::createUpStream,
NetworkUtils::updateUpStreamSuccess
};
CommandFunc NetworkUtils::sStartDhcpServerChain[] = {
NetworkUtils::setInterfaceUp,
NetworkUtils::startTethering,
NetworkUtils::setDhcpServerSuccess
};
CommandFunc NetworkUtils::sStopDhcpServerChain[] = {
NetworkUtils::stopTethering,
NetworkUtils::setDhcpServerSuccess
};
CommandFunc NetworkUtils::sNetworkInterfaceStatsChain[] = {
NetworkUtils::getRxBytes,
NetworkUtils::getTxBytes,
NetworkUtils::networkInterfaceStatsSuccess
};
CommandFunc NetworkUtils::sNetworkInterfaceEnableAlarmChain[] = {
NetworkUtils::enableAlarm,
NetworkUtils::setQuota,
NetworkUtils::setAlarm,
NetworkUtils::networkInterfaceAlarmSuccess
};
CommandFunc NetworkUtils::sNetworkInterfaceDisableAlarmChain[] = {
NetworkUtils::removeQuota,
NetworkUtils::disableAlarm,
NetworkUtils::networkInterfaceAlarmSuccess
};
CommandFunc NetworkUtils::sNetworkInterfaceSetAlarmChain[] = {
NetworkUtils::setAlarm,
NetworkUtils::networkInterfaceAlarmSuccess
};
/**
* Helper function to get the bit length from given mask.
*/
static uint32_t getMaskLength(const uint32_t mask)
{
uint32_t netmask = ntohl(mask);
uint32_t len = 0;
while (netmask & 0x80000000) {
len++;
netmask = netmask << 1;
}
return len;
}
/**
* Helper function to split string by seperator, store split result as an nsTArray.
*/
static void split(char* str, const char* sep, nsTArray<nsCString>& result)
{
char *s = strtok(str, sep);
while (s != nullptr) {
result.AppendElement(s);
s = strtok(nullptr, sep);
}
}
static void split(char* str, const char* sep, nsTArray<nsString>& result)
{
char *s = strtok(str, sep);
while (s != nullptr) {
result.AppendElement(NS_ConvertUTF8toUTF16(s));
s = strtok(nullptr, sep);
}
}
/**
* Helper function that implement join function.
*/
static void join(nsTArray<nsCString>& array, const char* sep, const uint32_t maxlen, char* result)
{
#define CHECK_LENGTH(len, add, max) len += add; \
if (len > max - 1) \
return; \
uint32_t len = 0;
uint32_t seplen = strlen(sep);
if (array.Length() > 0) {
CHECK_LENGTH(len, strlen(array[0].get()), maxlen)
strcpy(result, array[0].get());
for (uint32_t i = 1; i < array.Length(); i++) {
CHECK_LENGTH(len, seplen, maxlen)
strcat(result, sep);
CHECK_LENGTH(len, strlen(array[i].get()), maxlen)
strcat(result, array[i].get());
}
}
#undef CHECK_LEN
}
/**
* Helper function to get network interface properties from the system property table.
*/
static void getIFProperties(const char* ifname, IFProperties& prop)
{
char key[PROPERTY_KEY_MAX];
snprintf(key, PROPERTY_KEY_MAX - 1, "net.%s.gw", ifname);
property_get(key, prop.gateway, "");
snprintf(key, PROPERTY_KEY_MAX - 1, "net.%s.dns1", ifname);
property_get(key, prop.dns1, "");
snprintf(key, PROPERTY_KEY_MAX - 1, "net.%s.dns2", ifname);
property_get(key, prop.dns2, "");
}
static void postMessage(NetworkResultOptions& aResult)
{
MOZ_ASSERT(gNetworkUtils);
MOZ_ASSERT(gNetworkUtils->getMessageCallback());
if (*(gNetworkUtils->getMessageCallback()))
(*(gNetworkUtils->getMessageCallback()))(aResult);
}
static void postMessage(NetworkParams& aOptions, NetworkResultOptions& aResult)
{
MOZ_ASSERT(gNetworkUtils);
MOZ_ASSERT(gNetworkUtils->getMessageCallback());
aResult.mId = aOptions.mId;
if (*(gNetworkUtils->getMessageCallback()))
(*(gNetworkUtils->getMessageCallback()))(aResult);
}
void NetworkUtils::next(CommandChain* aChain, bool aError, NetworkResultOptions& aResult)
{
if (aError) {
ErrorCallback onError = aChain->getErrorCallback();
if(onError) {
aResult.mError = true;
(*onError)(aChain->getParams(), aResult);
}
delete aChain;
return;
}
CommandFunc f = aChain->getNextCommand();
if (!f) {
delete aChain;
return;
}
(*f)(aChain, next, aResult);
}
/**
* Send command to netd.
*/
void NetworkUtils::nextNetdCommand()
{
if (gCommandQueue.IsEmpty() || gPending) {
return;
}
gCurrentCommand.chain = GET_CURRENT_CHAIN;
gCurrentCommand.callback = GET_CURRENT_CALLBACK;
snprintf(gCurrentCommand.command, MAX_COMMAND_SIZE - 1, "%s", GET_CURRENT_COMMAND);
DEBUG("Sending \'%s\' command to netd.", gCurrentCommand.command);
SendNetdCommand(GET_CURRENT_NETD_COMMAND);
gCommandQueue.RemoveElementAt(0);
gPending = true;
}
/**
* Composite NetdCommand sent to netd
*
* @param aCommand Command sent to netd to execute.
* @param aChain Store command chain data, ex. command parameter.
* @param aCallback Callback function to be executed when the result of
* this command is returned from netd.
*/
void NetworkUtils::doCommand(const char* aCommand, CommandChain* aChain, CommandCallback aCallback)
{
DEBUG("Preparing to send \'%s\' command...", aCommand);
NetdCommand* netdCommand = new NetdCommand();
// Android JB version adds sequence number to netd command.
if (SDK_VERSION >= 16) {
snprintf((char*)netdCommand->mData, MAX_COMMAND_SIZE - 1, "0 %s", aCommand);
} else {
snprintf((char*)netdCommand->mData, MAX_COMMAND_SIZE - 1, "%s", aCommand);
}
netdCommand->mSize = strlen((char*)netdCommand->mData) + 1;
gCommandQueue.AppendElement(QueueData(netdCommand, aChain, aCallback));
nextNetdCommand();
}
/*
* Netd command function
*/
#define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aChain->getParams().prop).get()
#define GET_FIELD(prop) aChain->getParams().prop
void NetworkUtils::wifiFirmwareReload(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "softap fwreload %s %s", GET_CHAR(mIfname), GET_CHAR(mMode));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::startAccessPointDriver(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
// Skip the command for sdk version >= 16.
if (SDK_VERSION >= 16) {
aResult.mResultCode = 0;
aResult.mResultReason = NS_ConvertUTF8toUTF16("");
aCallback(aChain, false, aResult);
return;
}
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "softap start %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::stopAccessPointDriver(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
// Skip the command for sdk version >= 16.
if (SDK_VERSION >= 16) {
aResult.mResultCode = 0;
aResult.mResultReason = NS_ConvertUTF8toUTF16("");
aCallback(aChain, false, aResult);
return;
}
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "softap stop %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
/**
* Command format for sdk version < 16
* Arguments:
* argv[2] - wlan interface
* argv[3] - SSID
* argv[4] - Security
* argv[5] - Key
* argv[6] - Channel
* argv[7] - Preamble
* argv[8] - Max SCB
*
* Command format for sdk version >= 16
* Arguments:
* argv[2] - wlan interface
* argv[3] - SSID
* argv[4] - Security
* argv[5] - Key
*/
void NetworkUtils::setAccessPoint(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
if (SDK_VERSION >= 16) {
snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s \"%s\" %s \"%s\"",
GET_CHAR(mIfname),
GET_CHAR(mSsid),
GET_CHAR(mSecurity),
GET_CHAR(mKey));
} else {
snprintf(command, MAX_COMMAND_SIZE - 1, "softap set %s %s \"%s\" %s \"%s\" 6 0 8",
GET_CHAR(mIfname),
GET_CHAR(mWifictrlinterfacename),
GET_CHAR(mSsid),
GET_CHAR(mSecurity),
GET_CHAR(mKey));
}
doCommand(command, aChain, aCallback);
}
void NetworkUtils::cleanUpStream(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 0", GET_CHAR(mPreInternalIfname), GET_CHAR(mPreExternalIfname));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::createUpStream(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 0", GET_CHAR(mCurInternalIfname), GET_CHAR(mCurExternalIfname));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::startSoftAP(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
const char* command= "softap startap";
doCommand(command, aChain, aCallback);
}
void NetworkUtils::stopSoftAP(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
const char* command= "softap stopap";
doCommand(command, aChain, aCallback);
}
void NetworkUtils::getRxBytes(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "interface readrxcounter %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::getTxBytes(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
NetworkParams& options = aChain->getParams();
options.mRxBytes = atof(NS_ConvertUTF16toUTF8(aResult.mResultReason).get());
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "interface readtxcounter %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::enableAlarm(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
const char* command= "bandwidth enable";
doCommand(command, aChain, aCallback);
}
void NetworkUtils::disableAlarm(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
const char* command= "bandwidth disable";
doCommand(command, aChain, aCallback);
}
void NetworkUtils::setQuota(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setiquota %s %lld", GET_CHAR(mIfname), LLONG_MAX);
doCommand(command, aChain, aCallback);
}
void NetworkUtils::removeQuota(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth removeiquota %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::setAlarm(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "bandwidth setinterfacealert %s %ld", GET_CHAR(mIfname), GET_FIELD(mThreshold));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::setInterfaceUp(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
if (SDK_VERSION >= 16) {
snprintf(command, MAX_COMMAND_SIZE - 1, "interface setcfg %s %s %s %s",
GET_CHAR(mIfname),
GET_CHAR(mIp),
GET_CHAR(mPrefix),
GET_CHAR(mLink));
} else {
snprintf(command, MAX_COMMAND_SIZE - 1, "interface setcfg %s %s %s [%s]",
GET_CHAR(mIfname),
GET_CHAR(mIp),
GET_CHAR(mPrefix),
GET_CHAR(mLink));
}
doCommand(command, aChain, aCallback);
}
void NetworkUtils::tetherInterface(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface add %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::preTetherInterfaceList(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
if (SDK_VERSION >= 16) {
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface list");
} else {
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface list 0");
}
doCommand(command, aChain, aCallback);
}
void NetworkUtils::postTetherInterfaceList(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
// Send the dummy command to continue the function chain.
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
char buf[BUF_SIZE];
const char* reason = NS_ConvertUTF16toUTF8(aResult.mResultReason).get();
memcpy(buf, reason, strlen(reason));
split(buf, INTERFACE_DELIMIT, GET_FIELD(mInterfaceList));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::setIpForwardingEnabled(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
if (GET_FIELD(mEnable)) {
snprintf(command, MAX_COMMAND_SIZE - 1, "ipfwd enable");
} else {
// Don't disable ip forwarding because others interface still need it.
// Send the dummy command to continue the function chain.
if (GET_FIELD(mInterfaceList).Length() > 1) {
snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
} else {
snprintf(command, MAX_COMMAND_SIZE - 1, "ipfwd disable");
}
}
doCommand(command, aChain, aCallback);
}
void NetworkUtils::tetheringStatus(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
const char* command= "tether status";
doCommand(command, aChain, aCallback);
}
void NetworkUtils::stopTethering(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
// Don't stop tethering because others interface still need it.
// Send the dummy to continue the function chain.
if (GET_FIELD(mInterfaceList).Length() > 1) {
snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
} else {
snprintf(command, MAX_COMMAND_SIZE - 1, "tether stop");
}
doCommand(command, aChain, aCallback);
}
void NetworkUtils::startTethering(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
// We don't need to start tethering again.
// Send the dummy command to continue the function chain.
if (aResult.mResultReason.Find("started") != kNotFound) {
snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
} else {
snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s", GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp));
// If usbStartIp/usbEndIp is not valid, don't append them since
// the trailing white spaces will be parsed to extra empty args
// See: http://androidxref.com/4.3_r2.1/xref/system/core/libsysutils/src/FrameworkListener.cpp#78
if (!GET_FIELD(mUsbStartIp).IsEmpty() && !GET_FIELD(mUsbEndIp).IsEmpty()) {
snprintf(command, MAX_COMMAND_SIZE - 1, "%s %s %s", command, GET_CHAR(mUsbStartIp), GET_CHAR(mUsbEndIp));
}
}
doCommand(command, aChain, aCallback);
}
void NetworkUtils::untetherInterface(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface remove %s", GET_CHAR(mIfname));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::setDnsForwarders(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %s %s", GET_CHAR(mDns1), GET_CHAR(mDns2));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::enableNat(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "nat enable %s %s 0", GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
doCommand(command, aChain, aCallback);
}
void NetworkUtils::disableNat(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
char command[MAX_COMMAND_SIZE];
snprintf(command, MAX_COMMAND_SIZE - 1, "nat disable %s %s 0", GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
doCommand(command, aChain, aCallback);
}
#undef GET_CHAR
#undef GET_FIELD
/*
* Netd command success/fail function
*/
#define ASSIGN_FIELD(prop) aResult.prop = aChain->getParams().prop;
#define ASSIGN_FIELD_VALUE(prop, value) aResult.prop = value;
#define RUN_CHAIN(param, cmds, err) \
uint32_t size = sizeof(cmds) / sizeof(CommandFunc); \
CommandChain* chain = new CommandChain(param, cmds, size, err); \
NetworkResultOptions result; \
next(chain, false, result);
void NetworkUtils::wifiTetheringFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
{
// Notify the main thread.
postMessage(aOptions, aResult);
// If one of the stages fails, we try roll back to ensure
// we don't leave the network systems in limbo.
ASSIGN_FIELD_VALUE(mEnable, false)
RUN_CHAIN(aOptions, sWifiFailChain, nullptr)
}
void NetworkUtils::wifiTetheringSuccess(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
ASSIGN_FIELD(mEnable)
postMessage(aChain->getParams(), aResult);
}
void NetworkUtils::usbTetheringFail(NetworkParams& aOptions,
NetworkResultOptions& aResult)
{
// Notify the main thread.
postMessage(aOptions, aResult);
// Try to roll back to ensure
// we don't leave the network systems in limbo.
// This parameter is used to disable ipforwarding.
{
aOptions.mEnable = false;
RUN_CHAIN(aOptions, sUSBFailChain, nullptr)
}
// Disable usb rndis function.
{
NetworkParams options;
options.mEnable = false;
options.mReport = false;
gNetworkUtils->enableUsbRndis(options);
}
}
void NetworkUtils::usbTetheringSuccess(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
ASSIGN_FIELD(mEnable)
postMessage(aChain->getParams(), aResult);
}
void NetworkUtils::networkInterfaceStatsFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
{
postMessage(aOptions, aResult);
}
void NetworkUtils::networkInterfaceStatsSuccess(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
ASSIGN_FIELD(mRxBytes)
ASSIGN_FIELD_VALUE(mTxBytes, atof(NS_ConvertUTF16toUTF8(aResult.mResultReason).get()))
postMessage(aChain->getParams(), aResult);
}
void NetworkUtils::networkInterfaceAlarmFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
{
postMessage(aOptions, aResult);
}
void NetworkUtils::networkInterfaceAlarmSuccess(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
// TODO : error is not used , and it is conflict with boolean type error.
// params.error = parseFloat(params.resultReason);
postMessage(aChain->getParams(), aResult);
}
void NetworkUtils::updateUpStreamFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
{
postMessage(aOptions, aResult);
}
void NetworkUtils::updateUpStreamSuccess(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
ASSIGN_FIELD(mCurExternalIfname)
ASSIGN_FIELD(mCurInternalIfname)
postMessage(aChain->getParams(), aResult);
}
void NetworkUtils::setDhcpServerFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
{
aResult.mSuccess = false;
postMessage(aOptions, aResult);
}
void NetworkUtils::setDhcpServerSuccess(CommandChain* aChain, CommandCallback aCallback, NetworkResultOptions& aResult)
{
aResult.mSuccess = true;
postMessage(aChain->getParams(), aResult);
}
void NetworkUtils::wifiOperationModeFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
{
postMessage(aOptions, aResult);
}
void NetworkUtils::wifiOperationModeSuccess(CommandChain* aChain,
CommandCallback aCallback,
NetworkResultOptions& aResult)
{
postMessage(aChain->getParams(), aResult);
}
#undef ASSIGN_FIELD
#undef ASSIGN_FIELD_VALUE
NetworkUtils::NetworkUtils(MessageCallback aCallback)
: mMessageCallback(aCallback)
{
mNetUtils = new NetUtils();
char value[PROPERTY_VALUE_MAX];
property_get("ro.build.version.sdk", value, nullptr);
SDK_VERSION = atoi(value);
gNetworkUtils = this;
}
NetworkUtils::~NetworkUtils()
{
}
#define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get()
void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
{
bool ret = true;
if (aOptions.mCmd.EqualsLiteral("removeNetworkRoute")) {
removeNetworkRoute(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("setDNS")) {
setDNS(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("setDefaultRouteAndDNS")) {
setDefaultRouteAndDNS(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("removeDefaultRoute")) {
removeDefaultRoute(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("addHostRoute")) {
addHostRoute(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("removeHostRoute")) {
removeHostRoute(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("removeHostRoutes")) {
removeHostRoutes(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("getNetworkInterfaceStats")) {
getNetworkInterfaceStats(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("setNetworkInterfaceAlarm")) {
setNetworkInterfaceAlarm(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("enableNetworkInterfaceAlarm")) {
enableNetworkInterfaceAlarm(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("disableNetworkInterfaceAlarm")) {
disableNetworkInterfaceAlarm(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("setWifiOperationMode")) {
setWifiOperationMode(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("setDhcpServer")) {
setDhcpServer(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("setWifiTethering")) {
setWifiTethering(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("setUSBTethering")) {
setUSBTethering(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("enableUsbRndis")) {
enableUsbRndis(aOptions);
} else if (aOptions.mCmd.EqualsLiteral("updateUpStream")) {
updateUpStream(aOptions);
} else {
WARN("unknon message");
return;
}
if (!aOptions.mIsAsync) {
NetworkResultOptions result;
result.mRet = ret;
postMessage(aOptions, result);
}
}
/**
* Handle received data from netd.
*/
void NetworkUtils::onNetdMessage(NetdCommand* aCommand)
{
char* data = (char*)aCommand->mData;
// get code & reason.
char* result = strtok(data, NETD_MESSAGE_DELIMIT);
if (!result) {
nextNetdCommand();
return;
}
uint32_t code = atoi(result);
if (!isBroadcastMessage(code) && SDK_VERSION >= 16) {
strtok(nullptr, NETD_MESSAGE_DELIMIT);
}
char* reason = strtok(nullptr, "\0");
if (isBroadcastMessage(code)) {
DEBUG("Receiving broadcast message from netd.");
DEBUG(" ==> Code: %d Reason: %s", code, reason);
sendBroadcastMessage(code, reason);
nextNetdCommand();
return;
}
// Set pending to false before we handle next command.
DEBUG("Receiving \"%s\" command response from netd.", gCurrentCommand.command);
DEBUG(" ==> Code: %d Reason: %s", code, reason);
gReason.AppendElement(nsCString(reason));
// 1xx response code regards as command is proceeding, we need to wait for
// final response code such as 2xx, 4xx and 5xx before sending next command.
if (isProceeding(code)) {
return;
}
if (isComplete(code)) {
gPending = false;
}
if (gCurrentCommand.callback) {
char buf[BUF_SIZE];
join(gReason, INTERFACE_DELIMIT, BUF_SIZE, buf);
NetworkResultOptions result;
result.mResultCode = code;
result.mResultReason = NS_ConvertUTF8toUTF16(buf);
join(gReason, INTERFACE_DELIMIT, BUF_SIZE, buf);
(*gCurrentCommand.callback)(gCurrentCommand.chain, isError(code), result);
gReason.Clear();
}
// Handling pending commands if any.
if (isComplete(code)) {
nextNetdCommand();
}
}
/**
* Start/Stop DHCP server.
*/
bool NetworkUtils::setDhcpServer(NetworkParams& aOptions)
{
if (aOptions.mEnabled) {
aOptions.mWifiStartIp = aOptions.mStartIp;
aOptions.mWifiEndIp = aOptions.mEndIp;
aOptions.mIp = aOptions.mServerIp;
aOptions.mPrefix = aOptions.mMaskLength;
aOptions.mLink = NS_ConvertUTF8toUTF16("up");
RUN_CHAIN(aOptions, sStartDhcpServerChain, setDhcpServerFail)
} else {
RUN_CHAIN(aOptions, sStopDhcpServerChain, setDhcpServerFail)
}
return true;
}
/**
* Set DNS servers for given network interface.
*/
bool NetworkUtils::setDNS(NetworkParams& aOptions)
{
IFProperties interfaceProperties;
getIFProperties(GET_CHAR(mIfname), interfaceProperties);
if (aOptions.mDns1_str.IsEmpty()) {
property_set("net.dns1", interfaceProperties.dns1);
} else {
property_set("net.dns1", GET_CHAR(mDns1_str));
}
if (aOptions.mDns2_str.IsEmpty()) {
property_set("net.dns2", interfaceProperties.dns2);
} else {
property_set("net.dns2", GET_CHAR(mDns2_str));
}
// Bump the DNS change property.
char dnschange[PROPERTY_VALUE_MAX];
property_get("net.dnschange", dnschange, "0");
char num[PROPERTY_VALUE_MAX];
snprintf(num, PROPERTY_VALUE_MAX - 1, "%d", atoi(dnschange) + 1);
property_set("net.dnschange", num);
return true;
}
/**
* Set default route and DNS servers for given network interface.
*/
bool NetworkUtils::setDefaultRouteAndDNS(NetworkParams& aOptions)
{
if (!aOptions.mOldIfname.IsEmpty()) {
mNetUtils->do_ifc_remove_default_route(GET_CHAR(mOldIfname));
}
IFProperties ifprops;
getIFProperties(GET_CHAR(mIfname), ifprops);
if (aOptions.mGateway_str.IsEmpty()) {
mNetUtils->do_ifc_set_default_route(GET_CHAR(mIfname), inet_addr(ifprops.gateway));
} else {
mNetUtils->do_ifc_set_default_route(GET_CHAR(mIfname), inet_addr(GET_CHAR(mGateway_str)));
}
setDNS(aOptions);
return true;
}
/**
* Remove default route for given network interface.
*/
bool NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
{
mNetUtils->do_ifc_remove_default_route(GET_CHAR(mIfname));
return true;
}
/**
* Add host route for given network interface.
*/
bool NetworkUtils::addHostRoute(NetworkParams& aOptions)
{
uint32_t length = aOptions.mHostnames.Length();
for (uint32_t i = 0; i < length; i++) {
mNetUtils->do_ifc_add_route(GET_CHAR(mIfname), GET_CHAR(mHostnames[i]), 32, GET_CHAR(mGateway));
}
return true;
}
/**
* Remove host route for given network interface.
*/
bool NetworkUtils::removeHostRoute(NetworkParams& aOptions)
{
uint32_t length = aOptions.mHostnames.Length();
for (uint32_t i = 0; i < length; i++) {
mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname), GET_CHAR(mHostnames[i]), 32, GET_CHAR(mGateway));
}
return true;
}
/**
* Remove the routes associated with the named interface.
*/
bool NetworkUtils::removeHostRoutes(NetworkParams& aOptions)
{
mNetUtils->do_ifc_remove_host_routes(GET_CHAR(mIfname));
return true;
}
bool NetworkUtils::removeNetworkRoute(NetworkParams& aOptions)
{
uint32_t ip = inet_addr(GET_CHAR(mIp));
uint32_t netmask = inet_addr(GET_CHAR(mNetmask));
uint32_t subnet = ip & netmask;
uint32_t prefixLength = getMaskLength(netmask);
const char* gateway = "0.0.0.0";
struct in_addr addr;
addr.s_addr = subnet;
const char* dst = inet_ntoa(addr);
mNetUtils->do_ifc_remove_default_route(GET_CHAR(mIfname));
mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname), dst, prefixLength, gateway);
return true;
}
bool NetworkUtils::getNetworkInterfaceStats(NetworkParams& aOptions)
{
DEBUG("getNetworkInterfaceStats: %s", GET_CHAR(mIfname));
aOptions.mRxBytes = -1;
aOptions.mTxBytes = -1;
RUN_CHAIN(aOptions, sNetworkInterfaceStatsChain, networkInterfaceStatsFail);
return true;
}
bool NetworkUtils::setNetworkInterfaceAlarm(NetworkParams& aOptions)
{
DEBUG("setNetworkInterfaceAlarms: %s", GET_CHAR(mIfname));
RUN_CHAIN(aOptions, sNetworkInterfaceSetAlarmChain, networkInterfaceAlarmFail);
return true;
}
bool NetworkUtils::enableNetworkInterfaceAlarm(NetworkParams& aOptions)
{
DEBUG("enableNetworkInterfaceAlarm: %s", GET_CHAR(mIfname));
RUN_CHAIN(aOptions, sNetworkInterfaceEnableAlarmChain, networkInterfaceAlarmFail);
return true;
}
bool NetworkUtils::disableNetworkInterfaceAlarm(NetworkParams& aOptions)
{
DEBUG("disableNetworkInterfaceAlarms: %s", GET_CHAR(mIfname));
RUN_CHAIN(aOptions, sNetworkInterfaceDisableAlarmChain, networkInterfaceAlarmFail);
return true;
}
/**
* handling main thread's reload Wifi firmware request
*/
bool NetworkUtils::setWifiOperationMode(NetworkParams& aOptions)
{
DEBUG("setWifiOperationMode: %s %s", GET_CHAR(mIfname), GET_CHAR(mMode));
RUN_CHAIN(aOptions, sWifiOperationModeChain, wifiOperationModeFail);
return true;
}
/**
* handling main thread's enable/disable WiFi Tethering request
*/
bool NetworkUtils::setWifiTethering(NetworkParams& aOptions)
{
bool enable = aOptions.mEnable;
IFProperties interfaceProperties;
getIFProperties(GET_CHAR(mExternalIfname), interfaceProperties);
if (strcmp(interfaceProperties.dns1, "")) {
aOptions.mDns1 = NS_ConvertUTF8toUTF16(interfaceProperties.dns1);
}
if (strcmp(interfaceProperties.dns2, "")) {
aOptions.mDns2 = NS_ConvertUTF8toUTF16(interfaceProperties.dns2);
}
dumpParams(aOptions, "WIFI");
if (enable) {
DEBUG("Starting Wifi Tethering on %s <-> %s",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
RUN_CHAIN(aOptions, sWifiEnableChain, wifiTetheringFail)
} else {
DEBUG("Stopping Wifi Tethering on %s <-> %s",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
RUN_CHAIN(aOptions, sWifiDisableChain, wifiTetheringFail)
}
return true;
}
bool NetworkUtils::setUSBTethering(NetworkParams& aOptions)
{
bool enable = aOptions.mEnable;
IFProperties interfaceProperties;
getIFProperties(GET_CHAR(mExternalIfname), interfaceProperties);
if (strcmp(interfaceProperties.dns1, "")) {
aOptions.mDns1 = NS_ConvertUTF8toUTF16(interfaceProperties.dns1);
}
if (strcmp(interfaceProperties.dns2, "")) {
aOptions.mDns2 = NS_ConvertUTF8toUTF16(interfaceProperties.dns2);
}
dumpParams(aOptions, "USB");
if (enable) {
DEBUG("Starting USB Tethering on %s <-> %s",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
RUN_CHAIN(aOptions, sUSBEnableChain, usbTetheringFail)
} else {
DEBUG("Stopping USB Tethering on %s <-> %s",
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
RUN_CHAIN(aOptions, sUSBDisableChain, usbTetheringFail)
}
return true;
}
void NetworkUtils::checkUsbRndisState(NetworkParams& aOptions)
{
static uint32_t retry = 0;
char currentState[PROPERTY_VALUE_MAX];
property_get(SYS_USB_STATE_PROPERTY, currentState, nullptr);
nsTArray<nsCString> stateFuncs;
split(currentState, USB_CONFIG_DELIMIT, stateFuncs);
bool rndisPresent = stateFuncs.Contains(nsCString(USB_FUNCTION_RNDIS));
if (aOptions.mEnable == rndisPresent) {
NetworkResultOptions result;
result.mEnable = aOptions.mEnable;
result.mResult = true;
postMessage(aOptions, result);
retry = 0;
return;
}
if (retry < USB_FUNCTION_RETRY_TIMES) {
retry++;
usleep(USB_FUNCTION_RETRY_INTERVAL * 1000);
checkUsbRndisState(aOptions);
return;
}
NetworkResultOptions result;
result.mResult = false;
postMessage(aOptions, result);
retry = 0;
}
/**
* Modify usb function's property to turn on USB RNDIS function
*/
bool NetworkUtils::enableUsbRndis(NetworkParams& aOptions)
{
bool report = aOptions.mReport;
// For some reason, rndis doesn't play well with diag,modem,nmea.
// So when turning rndis on, we set sys.usb.config to either "rndis"
// or "rndis,adb". When turning rndis off, we go back to
// persist.sys.usb.config.
//
// On the otoro/unagi, persist.sys.usb.config should be one of:
//
// diag,modem,nmea,mass_storage
// diag,modem,nmea,mass_storage,adb
//
// When rndis is enabled, sys.usb.config should be one of:
//
// rdnis
// rndis,adb
//
// and when rndis is disabled, it should revert to persist.sys.usb.config
char currentConfig[PROPERTY_VALUE_MAX];
property_get(SYS_USB_CONFIG_PROPERTY, currentConfig, nullptr);
nsTArray<nsCString> configFuncs;
split(currentConfig, USB_CONFIG_DELIMIT, configFuncs);
char persistConfig[PROPERTY_VALUE_MAX];
property_get(PERSIST_SYS_USB_CONFIG_PROPERTY, persistConfig, nullptr);
nsTArray<nsCString> persistFuncs;
split(persistConfig, USB_CONFIG_DELIMIT, persistFuncs);
if (aOptions.mEnable) {
configFuncs.Clear();
configFuncs.AppendElement(nsCString(USB_FUNCTION_RNDIS));
if (persistFuncs.Contains(nsCString(USB_FUNCTION_ADB))) {
configFuncs.AppendElement(nsCString(USB_FUNCTION_ADB));
}
} else {
// We're turning rndis off, revert back to the persist setting.
// adb will already be correct there, so we don't need to do any
// further adjustments.
configFuncs = persistFuncs;
}
char newConfig[PROPERTY_VALUE_MAX] = "";
property_get(SYS_USB_CONFIG_PROPERTY, currentConfig, nullptr);
join(configFuncs, USB_CONFIG_DELIMIT, PROPERTY_VALUE_MAX, newConfig);
if (strcmp(currentConfig, newConfig)) {
property_set(SYS_USB_CONFIG_PROPERTY, newConfig);
}
// Trigger the timer to check usb state and report the result to NetworkManager.
if (report) {
usleep(USB_FUNCTION_RETRY_INTERVAL * 1000);
checkUsbRndisState(aOptions);
}
return true;
}
/**
* handling upstream interface change event.
*/
bool NetworkUtils::updateUpStream(NetworkParams& aOptions)
{
RUN_CHAIN(aOptions, sUpdateUpStreamChain, updateUpStreamFail)
return true;
}
void NetworkUtils::sendBroadcastMessage(uint32_t code, char* reason)
{
NetworkResultOptions result;
switch(code) {
case NETD_COMMAND_INTERFACE_CHANGE:
result.mTopic = NS_ConvertUTF8toUTF16("netd-interface-change");
break;
case NETD_COMMAND_BANDWIDTH_CONTROLLER:
result.mTopic = NS_ConvertUTF8toUTF16("netd-bandwidth-control");
break;
default:
return;
}
result.mBroadcast = true;
result.mReason = NS_ConvertUTF8toUTF16(reason);
postMessage(result);
}
inline uint32_t NetworkUtils::netdResponseType(uint32_t code)
{
return (code / 100) * 100;
}
inline bool NetworkUtils::isBroadcastMessage(uint32_t code)
{
uint32_t type = netdResponseType(code);
return type == NETD_COMMAND_UNSOLICITED;
}
inline bool NetworkUtils::isError(uint32_t code)
{
uint32_t type = netdResponseType(code);
return type != NETD_COMMAND_PROCEEDING && type != NETD_COMMAND_OKAY;
}
inline bool NetworkUtils::isComplete(uint32_t code)
{
uint32_t type = netdResponseType(code);
return type != NETD_COMMAND_PROCEEDING;
}
inline bool NetworkUtils::isProceeding(uint32_t code)
{
uint32_t type = netdResponseType(code);
return type == NETD_COMMAND_PROCEEDING;
}
void NetworkUtils::dumpParams(NetworkParams& aOptions, const char* aType)
{
#ifdef _DEBUG
DEBUG("Dump params:");
DEBUG(" ifname: %s", GET_CHAR(mIfname));
DEBUG(" ip: %s", GET_CHAR(mIp));
DEBUG(" link: %s", GET_CHAR(mLink));
DEBUG(" prefix: %s", GET_CHAR(mPrefix));
DEBUG(" wifiStartIp: %s", GET_CHAR(mWifiStartIp));
DEBUG(" wifiEndIp: %s", GET_CHAR(mWifiEndIp));
DEBUG(" usbStartIp: %s", GET_CHAR(mUsbStartIp));
DEBUG(" usbEndIp: %s", GET_CHAR(mUsbEndIp));
DEBUG(" dnsserver1: %s", GET_CHAR(mDns1));
DEBUG(" dnsserver2: %s", GET_CHAR(mDns2));
DEBUG(" internalIfname: %s", GET_CHAR(mInternalIfname));
DEBUG(" externalIfname: %s", GET_CHAR(mExternalIfname));
if (!strcmp(aType, "WIFI")) {
DEBUG(" wifictrlinterfacename: %s", GET_CHAR(mWifictrlinterfacename));
DEBUG(" ssid: %s", GET_CHAR(mSsid));
DEBUG(" security: %s", GET_CHAR(mSecurity));
DEBUG(" key: %s", GET_CHAR(mKey));
}
#endif
}
#undef GET_CHAR