在Android 平台中 WifiService 和 wpa_cli 都是 WAPS 的客户端, 他们在和 WPAS 交互时都会用到 wpa_supplicant 提供的接口。 这些接口全部声明在 wpa_ctrl.h 中,使用之前需要链接 libwpa_client.so 动态库, 另外需包含 wpa_ctrl.h 头文件。
LOCAL_SHARED_LIBRARIES += libwpa_client
#include "libwpa_client/wpa_ctrl.h"
// 连接 wpas , wpa_ctrl_open 等同于 wpa_ctrl_open2(ctrl_path, NULL)
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path);
// 断开连接
void wpa_ctrl_close(struct wpa_ctrl *ctrl);
// 向 wpas 发送 command ,请求的事件通过 reply 返回
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
char *reply, size_t *reply_len,
void (*msg_cb)(char *msg, size_t len));
// 设置接收 wpas unsolicited event
int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
客户端使用wpa_ctrl时首先要连接 wpa_supplicant 获取到控制对象。
WPAS 回报的事件有两种:
solicited event(有请求的事件), 会通过 wpa_ctrl_request 的 reply 参数返回 ;
另一种是 unsolicited event(未请求的事件),在某些过程中 WPAS 可能会回报一些 unsolicited event, 而这些 unsolicited event 会在 wpa_ctrl_request 的 msg_cb 中处理, 但这些事件默认是不会监听的,需先调用 wpa_ctrl_attach 来设置监听。
以下以 wpa_cli 为例 :
static int wpa_cli_open_connection(const char *ifname, int attach)
{
// 创建发送 command 的连接 , Android 中 cfile 为 /data/misc/wifi/sockets/wlan0
ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
// 创建监听 unsolicited event 的连接
if (attach && interactive)
mon_conn = wpa_ctrl_open2(cfile, client_socket_dir);
else
mon_conn = NULL;
if (mon_conn) {
// 设置监听 unsolicited event
if (wpa_ctrl_attach(mon_conn) == 0) {
wpa_cli_attached = 1;
......
}
}
return 0;
}
下面再来看一下是如何向 wpas 发送 command :
static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
{
char buf[4096];
size_t len;
int ret;
if (ifname_prefix) {
os_snprintf(buf, sizeof(buf), "IFNAME=%s %s",
ifname_prefix, cmd);
buf[sizeof(buf) - 1] = '\0';
cmd = buf;
}
len = sizeof(buf) - 1;
ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
wpa_cli_msg_cb);
if (print) {
buf[len] = '\0';
// buf 中会包含 solicited event
printf("%s", buf);
if (interactive && len > 0 && buf[len - 1] != '\n')
printf("\n");
}
return 0;
}
这里的 cmd 为需要执行的 command , 例如 “scan” , “scan_results” 等, 相对应的 buf 中会被写入 command 的执行结果, 例如 “scan” 成功则直接返回 “OK” , 而 “scan_results” 会返回搜索到的 bssid 及信息。
再来看一下 unsolicited event 的监听 , 以下两个API用于 开启和关闭 unsolicited event 的监听:
int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
而如果用户不想下 command ,只想去接收 unsolicited event ,可通过 wpa_ctrl_recv 函数
int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
同样来看 wpa_cli 的代码 :
static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
{
if (ctrl_conn == NULL) {
wpa_cli_reconnect();
return;
}
// 检测是否有可读事件,否则阻塞
while (wpa_ctrl_pending(ctrl) > 0) {
char buf[4096];
size_t len = sizeof(buf) - 1;
if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
// buf 中为 wpas 出来的 event
buf[len] = '\0';
......
}
} else {
printf("Could not read pending message.\n");
break;
}
}
......
}