Bug 774582 - Unable to know if there is a user connected to Wifi tethering network. r=mrbkap

This commit is contained in:
Vincent Chang 2014-01-22 16:37:40 +08:00
Родитель 8c8afe75e9
Коммит 07345883f9
15 изменённых файлов: 404 добавлений и 3 удалений

Просмотреть файл

@ -285,6 +285,10 @@ const kEventConstructors = {
return new MozWifiStatusChangeEvent(aName, aProps);
},
},
MozWifiStationInfoEvent: { create: function (aName, aProps) {
return new MozWifiStationInfoEvent(aName, aProps);
},
},
MutationEvent: { create: function (aName, aProps) {
var e = document.createEvent("mutationevent");
e.initMutationEvent(aName, aProps.bubbles, aProps.cancelable,

Просмотреть файл

@ -713,7 +713,9 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiConnectionInfoEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiManager", b2g: true, permission: "wifi-manage"},
{name: "MozWifiStationInfoEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiManager", b2g: true, permission: "wifi-manage"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozWifiNetwork", b2g: true, permission: "wifi-manage"},
// IMPORTANT: Do not change this list without review from a DOM peer!

Просмотреть файл

@ -321,4 +321,10 @@ interface MozWifiManager : EventTarget {
*/
attribute EventHandler onenabled;
attribute EventHandler ondisabled;
/**
* An event listener that is called with information about the number
* of wifi stations connected to wifi hotspot every 5 seconds.
*/
attribute EventHandler onstationInfoUpdate;
};

Просмотреть файл

@ -0,0 +1,19 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
[Constructor(DOMString type, optional MozWifiStationInfoEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
interface MozWifiStationInfoEvent : Event
{
/**
* The number of wifi stations connected to wifi hotspot.
*/
readonly attribute short station;
};
dictionary MozWifiStationInfoEventInit : EventInit
{
short station = 0;
};

Просмотреть файл

@ -595,6 +595,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
'MozWifiManager.webidl',
'MozWifiP2pManager.webidl',
'MozWifiP2pStatusChangeEvent.webidl',
'MozWifiStationInfoEvent.webidl',
'MozWifiStatusChangeEvent.webidl',
]
else:

Просмотреть файл

@ -80,6 +80,7 @@ function DOMWifiManager() {
this.defineEventHandlerGetterSetter("onconnectionInfoUpdate");
this.defineEventHandlerGetterSetter("onenabled");
this.defineEventHandlerGetterSetter("ondisabled");
this.defineEventHandlerGetterSetter("onstationInfoUpdate");
}
DOMWifiManager.prototype = {
@ -99,6 +100,7 @@ DOMWifiManager.prototype = {
this._enabled = false;
this._lastConnectionInfo = null;
this._capabilities = null;
this._stationNumber = 0;
const messages = ["WifiManager:getNetworks:Return:OK", "WifiManager:getNetworks:Return:NO",
"WifiManager:getKnownNetworks:Return:OK", "WifiManager:getKnownNetworks:Return:NO",
@ -116,8 +118,8 @@ DOMWifiManager.prototype = {
"WifiManager:onconnect", "WifiManager:ondisconnect",
"WifiManager:onwpstimeout", "WifiManager:onwpsfail",
"WifiManager:onwpsoverlap", "WifiManager:connectionInfoUpdate",
"WifiManager:onauthenticating",
"WifiManager:onconnectingfailed"];
"WifiManager:onauthenticating", "WifiManager:onconnectingfailed",
"WifiManager:stationInfoUpdate"];
this.initDOMRequestHelper(aWindow, messages);
this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
@ -392,6 +394,10 @@ DOMWifiManager.prototype = {
this._connectionStatus = "authenticating";
this._fireStatusChangeEvent();
break;
case "WifiManager:stationInfoUpdate":
this._stationNumber = msg.station;
this._fireStationInfoUpdate(msg);
break;
}
},
@ -419,6 +425,13 @@ DOMWifiManager.prototype = {
this.__DOM_IMPL__.dispatchEvent(evt);
},
_fireStationInfoUpdate: function onStationInfoUpdate(info) {
var evt = new this._window.MozWifiStationInfoEvent("stationInfoUpdate",
{ station: this._stationNumber}
);
this.__DOM_IMPL__.dispatchEvent(evt);
},
getNetworks: function getNetworks() {
var request = this.createRequest();
this._sendMessageForRequest("WifiManager:getNetworks", null, request);

Просмотреть файл

@ -276,6 +276,33 @@ this.WifiCommand = function(aControlMessage, aInterface, aSdkVersion) {
});
};
command.connectToHostapd = function(callback) {
voidControlMessage("connect_to_hostapd", callback);
};
command.closeHostapdConnection = function(callback) {
voidControlMessage("close_hostapd_connection", callback);
};
command.hostapdCommand = function (callback, request) {
var msg = { cmd: "hostapd_command",
request: request,
iface: aInterface };
aControlMessage(msg, function(data) {
callback(data.status ? null : data.reply);
});
};
command.hostapdGetStations = function (callback) {
var msg = { cmd: "hostapd_get_stations",
iface: aInterface };
aControlMessage(msg, function(data) {
callback(data.status);
});
};
command.setPowerModeICS = function (mode, callback) {
doBooleanCommand("DRIVER POWERMODE " + (mode === "AUTO" ? 0 : 1), "OK", callback);
};

Просмотреть файл

@ -0,0 +1,176 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WifiHotspotUtils.h"
#include <dlfcn.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include <cutils/properties.h>
#include "prinit.h"
#include "mozilla/Assertions.h"
#include "nsDebug.h"
#include "nsPrintfCString.h"
static void* sWifiHotspotUtilsLib;
static PRCallOnceType sInitWifiHotspotUtilsLib;
// Socket pair used to exit from a blocking read.
static struct wpa_ctrl* ctrl_conn;
static const char *ctrl_iface_dir = "/data/misc/wifi/hostapd";
static char *ctrl_ifname = nullptr;
DEFINE_DLFUNC(wpa_ctrl_open, struct wpa_ctrl*, const char*)
DEFINE_DLFUNC(wpa_ctrl_close, void, struct wpa_ctrl*)
DEFINE_DLFUNC(wpa_ctrl_attach, int32_t, struct wpa_ctrl*)
DEFINE_DLFUNC(wpa_ctrl_detach, int32_t, struct wpa_ctrl*)
DEFINE_DLFUNC(wpa_ctrl_request, int32_t, struct wpa_ctrl*,
const char*, size_t cmd_len, char *reply,
size_t *reply_len, void (*msg_cb)(char *msg, size_t len))
static PRStatus
InitWifiHotspotUtilsLib()
{
sWifiHotspotUtilsLib = dlopen("/system/lib/libwpa_client.so", RTLD_LAZY);
// We might fail to open the hardware lib. That's OK.
return PR_SUCCESS;
}
static void*
GetWifiHotspotLibHandle()
{
PR_CallOnce(&sInitWifiHotspotUtilsLib, InitWifiHotspotUtilsLib);
return sWifiHotspotUtilsLib;
}
struct wpa_ctrl *
WifiHotspotUtils::openConnection(const char *ifname)
{
if (!ifname) {
return nullptr;
}
USE_DLFUNC(wpa_ctrl_open)
ctrl_conn = wpa_ctrl_open(nsPrintfCString("%s/%s", ctrl_iface_dir, ifname).get());
return ctrl_conn;
}
int32_t
WifiHotspotUtils::sendCommand(struct wpa_ctrl *ctrl, const char *cmd,
char *reply, size_t *reply_len)
{
int32_t ret;
if (!ctrl_conn) {
NS_WARNING(nsPrintfCString("Not connected to hostapd - \"%s\" command dropped.\n", cmd).get());
return -1;
}
USE_DLFUNC(wpa_ctrl_request)
ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), reply, reply_len, nullptr);
if (ret == -2) {
NS_WARNING(nsPrintfCString("'%s' command timed out.\n", cmd).get());
return -2;
} else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) {
return -1;
}
if (strncmp(cmd, "PING", 4) == 0) {
reply[*reply_len] = '\0';
}
return 0;
}
// static
void*
WifiHotspotUtils::GetSharedLibrary()
{
void* wpaClientLib = GetWifiHotspotLibHandle();
if (!wpaClientLib) {
NS_WARNING("No /system/lib/libwpa_client.so");
}
return wpaClientLib;
}
int32_t WifiHotspotUtils::do_wifi_connect_to_hostapd()
{
struct dirent *dent;
DIR *dir = opendir(ctrl_iface_dir);
if (dir) {
while ((dent = readdir(dir))) {
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0) {
continue;
}
ctrl_ifname = strdup(dent->d_name);
break;
}
closedir(dir);
}
ctrl_conn = openConnection(ctrl_ifname);
if (!ctrl_conn) {
NS_WARNING(nsPrintfCString("Unable to open connection to hostapd on \"%s\": %s",
ctrl_ifname, strerror(errno)).get());
return -1;
}
USE_DLFUNC(wpa_ctrl_attach)
if (wpa_ctrl_attach(ctrl_conn) != 0) {
USE_DLFUNC(wpa_ctrl_close)
wpa_ctrl_close(ctrl_conn);
ctrl_conn = nullptr;
return -1;
}
return 0;
}
int32_t WifiHotspotUtils::do_wifi_close_hostapd_connection()
{
if (!ctrl_conn) {
NS_WARNING("Invalid ctrl_conn.");
return -1;
}
USE_DLFUNC(wpa_ctrl_detach)
if (wpa_ctrl_detach(ctrl_conn) < 0) {
NS_WARNING("Failed to detach wpa_ctrl.");
}
USE_DLFUNC(wpa_ctrl_close)
wpa_ctrl_close(ctrl_conn);
ctrl_conn = nullptr;
return 0;
}
int32_t WifiHotspotUtils::do_wifi_hostapd_command(const char *command,
char *reply,
size_t *reply_len)
{
return sendCommand(ctrl_conn, command, reply, reply_len);
}
int32_t WifiHotspotUtils::do_wifi_hostapd_get_stations()
{
char addr[32], cmd[64];
int stations = 0;
size_t addrLen = sizeof(addr);
if (sendCommand(ctrl_conn, "STA-FIRST", addr, &addrLen)) {
return 0;
}
stations++;
sprintf(cmd, "STA-NEXT %s", addr);
while (sendCommand(ctrl_conn, cmd, addr, &addrLen) == 0) {
stations++;
sprintf(cmd, "STA-NEXT %s", addr);
}
return stations;
}

Просмотреть файл

@ -0,0 +1,44 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Abstraction on top of the network support from libnetutils that we
* use to set up network connections.
*/
#ifndef WifiHotspotUtils_h
#define WifiHotspotUtils_h
// Forward declaration.
struct wpa_ctrl;
class WifiHotspotUtils
{
public:
static void* GetSharedLibrary();
int32_t do_wifi_connect_to_hostapd();
int32_t do_wifi_close_hostapd_connection();
int32_t do_wifi_hostapd_command(const char *command,
char *reply,
size_t *reply_len);
int32_t do_wifi_hostapd_get_stations();
private:
struct wpa_ctrl * openConnection(const char *ifname);
int32_t sendCommand(struct wpa_ctrl *ctrl, const char *cmd,
char *reply, size_t *reply_len);
};
// Defines a function type with the right arguments and return type.
#define DEFINE_DLFUNC(name, ret, args...) typedef ret (*FUNC##name)(args);
// Set up a dlsymed function ready to use.
#define USE_DLFUNC(name) \
FUNC##name name = (FUNC##name) dlsym(GetSharedLibrary(), #name); \
if (!name) { \
MOZ_ASSUME_UNREACHABLE("Symbol not found in shared library : " #name); \
}
#endif // WifiHotspotUtils_h

Просмотреть файл

@ -381,6 +381,7 @@ WpaSupplicant::WpaSupplicant()
mImpl = new KKWpaSupplicantImpl();
}
mNetUtils = new NetUtils();
mWifiHotspotUtils = new WifiHotspotUtils();
};
void WpaSupplicant::WaitForEvent(nsAString& aEvent, const nsCString& aInterface)
@ -414,6 +415,10 @@ bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions,
return false;
}
if (!mWifiHotspotUtils->GetSharedLibrary()) {
return false;
}
// Always correlate the opaque ids.
aResult.mId = aOptions.mId;
@ -518,6 +523,51 @@ bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions,
if (inet_ntop(AF_INET, &aResult.mMask, inet_str, sizeof(inet_str))) {
aResult.mMask_str = NS_ConvertUTF8toUTF16(inet_str);
}
} else if (aOptions.mCmd.EqualsLiteral("hostapd_command")) {
size_t len = BUFFER_SIZE - 1;
char buffer[BUFFER_SIZE];
NS_ConvertUTF16toUTF8 request(aOptions.mRequest);
aResult.mStatus = mWifiHotspotUtils->do_wifi_hostapd_command(request.get(),
buffer,
&len);
nsString value;
if (aResult.mStatus == 0) {
if (buffer[len - 1] == '\n') { // remove trailing new lines.
len--;
}
buffer[len] = '\0';
CheckBuffer(buffer, len, value);
}
aResult.mReply = value;
} else if (aOptions.mCmd.EqualsLiteral("hostapd_get_stations")) {
aResult.mStatus = mWifiHotspotUtils->do_wifi_hostapd_get_stations();
} else if (aOptions.mCmd.EqualsLiteral("connect_to_hostapd")) {
aResult.mStatus = mWifiHotspotUtils->do_wifi_connect_to_hostapd();
} else if (aOptions.mCmd.EqualsLiteral("close_hostapd_connection")) {
aResult.mStatus = mWifiHotspotUtils->do_wifi_close_hostapd_connection();
} else if (aOptions.mCmd.EqualsLiteral("hostapd_command")) {
size_t len = BUFFER_SIZE - 1;
char buffer[BUFFER_SIZE];
NS_ConvertUTF16toUTF8 request(aOptions.mRequest);
aResult.mStatus = mWifiHotspotUtils->do_wifi_hostapd_command(request.get(),
buffer,
&len);
nsString value;
if (aResult.mStatus == 0) {
if (buffer[len - 1] == '\n') { // remove trailing new lines.
len--;
}
buffer[len] = '\0';
CheckBuffer(buffer, len, value);
}
aResult.mReply = value;
} else if (aOptions.mCmd.EqualsLiteral("hostapd_get_stations")) {
aResult.mStatus = mWifiHotspotUtils->do_wifi_hostapd_get_stations();
} else if (aOptions.mCmd.EqualsLiteral("connect_to_hostapd")) {
aResult.mStatus = mWifiHotspotUtils->do_wifi_connect_to_hostapd();
} else if (aOptions.mCmd.EqualsLiteral("close_hostapd_connection")) {
aResult.mStatus = mWifiHotspotUtils->do_wifi_close_hostapd_connection();
} else {
NS_WARNING("WpaSupplicant::ExecuteCommand : Unknown command");
printf_stderr("WpaSupplicant::ExecuteCommand : Unknown command: %s",

Просмотреть файл

@ -14,6 +14,7 @@
#include "nsAutoPtr.h"
#include "mozilla/dom/WifiOptionsBinding.h"
#include "mozilla/dom/network/NetUtils.h"
#include "WifiHotspotUtils.h"
#include "nsCxPusher.h"
// Needed to add a copy constructor to WifiCommandOptions.
@ -131,6 +132,7 @@ public:
private:
nsAutoPtr<WpaSupplicantImpl> mImpl;
nsAutoPtr<NetUtils> mNetUtils;
nsAutoPtr<WifiHotspotUtils> mWifiHotspotUtils;
protected:
void CheckBuffer(char* buffer, int32_t length, nsAString& aEvent);

Просмотреть файл

@ -1059,6 +1059,19 @@ var WifiManager = (function() {
}
}
var wifiHotspotStatusTimer = null;
function cancelWifiHotspotStatusTimer() {
if (wifiHotspotStatusTimer) {
wifiHotspotStatusTimer.cancel();
wifiHotspotStatusTimer = null;
}
}
function createWifiHotspotStatusTimer(onTimeout) {
wifiHotspotStatusTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
wifiHotspotStatusTimer.init(onTimeout, 5000, Ci.nsITimer.TYPE_REPEATING_SLACK);
}
// Get wifi interface and load wifi driver when enable Ap mode.
manager.setWifiApEnabled = function(enabled, configuration, callback) {
if (enabled === manager.isWifiTetheringEnabled(manager.tetheringState)) {
@ -1072,9 +1085,20 @@ var WifiManager = (function() {
if (status < 0) {
callback();
manager.tetheringState = "UNINITIALIZED";
if (wifiHotspotStatusTimer) {
cancelWifiHotspotStatusTimer();
wifiCommand.closeHostapdConnection(function(result) {
});
}
return;
}
function getWifiHotspotStatus() {
wifiCommand.hostapdGetStations(function(result) {
notify("stationInfoUpdate", {station: result});
});
}
function doStartWifiTethering() {
cancelWaitForDriverReadyTimer();
WifiNetworkInterface.name = manager.ifname;
@ -1084,6 +1108,13 @@ var WifiManager = (function() {
manager.tetheringState = "UNINITIALIZED";
} else {
manager.tetheringState = "COMPLETED";
wifiCommand.connectToHostapd(function(result) {
if (result) {
return;
}
// Create a timer to track the connection status.
createWifiHotspotStatusTimer(getWifiHotspotStatus);
});
}
// Pop out current request.
callback();
@ -2282,6 +2313,10 @@ function WifiWorker() {
});
};
WifiManager.onstationInfoUpdate = function() {
self._fireEvent("stationInfoUpdate", { station: this.station });
};
// Read the 'wifi.enabled' setting in order to start with a known
// value at boot time. The handle() will be called after reading.
//

Просмотреть файл

@ -7,6 +7,7 @@
XPIDL_SOURCES += [
'nsIDOMMozWifiConnectionInfoEvent.idl',
'nsIDOMMozWifiP2pStatusChangeEvent.idl',
'nsIDOMMozWifiStationInfoEvent.idl',
'nsIDOMMozWifiStatusChangeEvent.idl',
'nsIWifi.idl',
'nsIWifiCertService.idl',
@ -35,6 +36,7 @@ EXTRA_JS_MODULES += [
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
SOURCES = [
'WifiCertService.cpp',
'WifiHotspotUtils.cpp',
'WifiProxyService.cpp',
'WifiUtils.cpp',
]

Просмотреть файл

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIDOMEvent.idl"
[scriptable, builtinclass, uuid(97dc8040-d5c9-11e3-9c1a-0800200c9a66)]
interface nsIDOMMozWifiStationInfoEvent : nsIDOMEvent
{
/**
* The number of wifi stations connected to wifi hotspot.
*/
readonly attribute short station;
[noscript] void initMozWifiStationInfoEvent(in DOMString aType,
in boolean aCanBubble,
in boolean aCancelable,
in short station);
};

Просмотреть файл

@ -26,6 +26,7 @@ simple_events = [
'MozWifiP2pStatusChangeEvent',
'MozWifiStatusChangeEvent',
'MozWifiConnectionInfoEvent',
'MozWifiStationInfoEvent',
#endif
#ifdef MOZ_B2G_RIL
'MozCellBroadcastEvent',