SoftAP 打开时调用的相关函数解析
Softap字面意思是用软件实现AP的功能,让你的移动设备可以作为一个路由,让别的站点链接。比如让别人的手机连上你的已经打开AP功能的手机,玩联机游戏或者上网等等
但事实上此功能是需要硬件以及驱动的支持才能真正的实现的。
Softap打开流程。
在Android系统的Setting界面的wireless配置项中会看到一个“Portable Wi-Fi hotspot” 跟一个"Configure Wi-Fi hotspot setting"选项,可以进入系统配置AP的名称,加密方式,密码等。 如下图
当你做完这些设置,系统接受的AP设置界面变化打开的响应,从此开启了整个Android SoftAP的序幕。
首先./packages/apps/Settings/src/com/android/settings/TetherSettings.java 的onPreferenceChange 函数接收到Softap状态改变信息
public boolean onPreferenceChange(Preference preference, Object value) {
boolean enable = (Boolean) value;
if (enable) {
startProvisioningIfNecessary(WIFI_TETHERING);
} else {
mWifiApEnabler.setSoftapEnabled(false);
}
return false;
}
Softap开启时,enable 为真,因而执行startProvisioningIfNecessary(WIFI_TETHERING);
private void startProvisioningIfNecessary(int choice) {
mTetherChoice = choice;
if (isProvisioningNeeded()) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName(mProvisionApp[0], mProvisionApp[1]);
startActivityForResult(intent, PROVISION_REQUEST);
} else {
startTethering();
}
}
isProvisioningNeeded 用来检测是否需要进行一些准备工作
如果无需准备工作则执行startTethering 大戏即将上演了 期待ing
private void startTethering() {
switch (mTetherChoice) {
case WIFI_TETHERING:
mWifiApEnabler.setSoftapEnabled(true);
break;
case BLUETOOTH_TETHERING:
// turn on Bluetooth first
break;
case USB_TETHERING:
setUsbTethering(true);
break;
default:
//should not happen
break;
}
}
这里 mTetherChoice == WIFI_TETHERING 所以继而执行WiFiApEnable.java中的setSoftapEnabled(true)函数
也从此处也跳出了Setting的代码 跳入了Android WIFI 子系统的framework层
./packages/apps/Settings/src/com/android/settings/wifi/WifiApEnabler.java
public void setSoftapEnabled(boolean enable) {
final ContentResolver cr = mContext.getContentResolver();
/**
* Disable Wifi if enabling tethering
*/
int wifiState = mWifiManager.getWifiState(); //获取当前wifi的状态 如果开启则关闭且保存状态信息到变量中
if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) ||
(wifiState == WifiManager.WIFI_STATE_ENABLED))) {
mWifiManager.setWifiEnabled(false);
Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 1);
}
if (mWifiManager.setWifiApEnabled(null, enable)) {
/* Disable here, enabled on receiving success broadcast */
mCheckBox.setEnabled(false);
} else {
mCheckBox.setSummary(R.string.wifi_error);
}
/**
* If needed, restore Wifi on tether disable
*/
if (!enable) {
int wifiSavedState = 0;
try {
wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE);
} catch (Settings.SettingNotFoundException e) {
;
}
if (wifiSavedState == 1) {
mWifiManager.setWifiEnabled(true);
Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
}
}
}
上面的代码中我们看到了Google人的考虑事情的周全。首先检测Wifi当前状态如果正在打开或者已经打开则关闭WIFI并将此状态记录下来,以便关闭softap时它能自动恢复到之前打开wifi的状态。 Android代码不愧牛X,这些都能想到... 崇拜那些大牛。
这里调用mWifiManager.setWifiApEnabled(null, enable) "frameworks/base/wifi/java/android/net/wifi/WifiManager.java"
public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
try {
mService.setWifiApEnabled(wifiConfig, enabled);
return true;
} catch (RemoteException e) {
return false;
}
}
转向服务层的 setWifiApEnabled "frameworks/base/services/java/com/android/server/WifiService.java"
public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
enforceChangePermission();
mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
}
从而调用到最基础的也是最重要的Wifi状态机中的 setWifiApEnabled 实例 其实我真搞不懂为什么Android代码要嵌套这么多层去调用,为了安全、方便... 哪个牛人解释一下。
"frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java"
public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
mLastApEnableUid.set(Binder.getCallingUid());
if (enable) {
/* Argument is the state that is entered prior to load */
sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
} else {
sendMessage(CMD_STOP_AP);
/* Argument is the state that is entered upon success */
sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
}
}
发送CMD_LOAD_DRIVER状态迁移到mDriverLoadingState 加载AP对应的驱动 这里把WIFI的驱动跟 AP的驱动做了区分,可见SoftAP不仅仅是软件实现的,需要硬件驱动的相应支持。
class DriverLoadingState extends State {
@Override
public void enter() {
new Thread(new Runnable() {
public void run() {
mWakeLock.acquire();
//enabling state
switch(message.arg1) {
case WIFI_STATE_ENABLING:
setWifiState(WIFI_STATE_ENABLING);
break;
case WIFI_AP_STATE_ENABLING:
setWifiApState(WIFI_AP_STATE_ENABLING);
break;
}
if(mWifiNative.loadDriver()) {
if (DBG) log("Driver load successful");
sendMessage(CMD_LOAD_DRIVER_SUCCESS);
} else {
loge("Failed to load driver!");
switch(message.arg1) {
case WIFI_STATE_ENABLING:
setWifiState(WIFI_STATE_UNKNOWN);
break;
case WIFI_AP_STATE_ENABLING:
setWifiApState(WIFI_AP_STATE_FAILED);
break;
}
sendMessage(CMD_LOAD_DRIVER_FAILURE);
}
mWakeLock.release();
}
}).start();
}
@Override
public boolean processMessage(Message message) {
if (DBG) log(getName() + message.toString() + "\n");
switch (message.what) {
case CMD_LOAD_DRIVER_SUCCESS:
transitionTo(mDriverLoadedState);
break;
case CMD_LOAD_DRIVER_FAILURE:
transitionTo(mDriverFailedState);
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}
加载驱动成功后 系统迁移到mDriverLoadedState 状态
接收到 CMD_START_AP消息 状态又被迁移至mSoftApStartingState
class DriverLoadedState extends State {
@Override
public void enter() {
if (DBG) log(getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
}
@Override
public boolean processMessage(Message message) {
if (DBG) log(getName() + message.toString() + "\n");
switch(message.what) {
/*
******
*/
case CMD_START_AP:
transitionTo(mSoftApStartingState);
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
}
SoftApStartingState 会检测上层传下的参数的有效性并调用startSoftApWithConfig 配置、打开SoftAP
class SoftApStartingState extends State {
@Override
public void enter() {
if (DBG) log(getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
final Message message = getCurrentMessage();
if (message.what == CMD_START_AP) {
final WifiConfiguration config = (WifiConfiguration) message.obj;
if (config == null) {
mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
} else {
mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
startSoftApWithConfig(config);
}
} else {
throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
}
}
@Override
public boolean processMessage(Message message) {
if (DBG) log(getName() + message.toString() + "\n");
switch(message.what) {
case CMD_LOAD_DRIVER:
case CMD_UNLOAD_DRIVER:
//....
case CMD_STOP_SUPPLICANT:
case CMD_START_AP:
//....
}
}
}
获取SoftAp的网络配置AP名称 加密方式密码....
进行系统驱动(硬件)的配置。
private void startSoftApWithConfig(final WifiConfiguration config) {
// start hostapd on a seperate thread
new Thread(new Runnable() {
public void run() {
try {
mNwService.startAccessPoint(config, mInterfaceName);
} catch (Exception e) {
loge("Exception in softap start " + e);
try {
mNwService.stopAccessPoint(mInterfaceName);
mNwService.startAccessPoint(config, mInterfaceName);
} catch (Exception e1) {
loge("Exception in softap re-start " + e1);
sendMessage(CMD_START_AP_FAILURE);
return;
}
}
if (DBG) log("Soft AP start successful");
sendMessage(CMD_START_AP_SUCCESS);
}
}).start();
}
//...
}
这里调用到了"frameworks/base/services/java/com/android/server/NetworkManagementService.java" 中的startAccessPoint函数
函数如下:
public void startAccessPoint(
WifiConfiguration wifiConfig, String wlanIface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
wifiFirmwareReload(wlanIface, "AP");
if (wifiConfig == null) {
mConnector.execute("softap", "set", wlanIface);
} else {
mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
getSecurityType(wifiConfig), wifiConfig.preSharedKey);
}
mConnector.execute("softap", "startap");
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
1、下载AP对应的 firmware
wifiFirmwareReload(wlanIface, "AP");
2、设置ap的ssid 加密方式 以及密码
mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, getSecurityType(wifiConfig), wifiConfig.preSharedKey);
3、运行softap
mConnector.execute("softap", "startap");
这里通过一个NativeDaemonConnector的实例mConnector 调用c++程序 具体的实现我是没看懂 但是知道最后实际调用的函数, 想深入了解可以找一些其他的资料看
实际调用到了 "./system/netd/CommandListener.cpp" 中的CommandListener::SoftapCmd::runCommand
int CommandListener::SoftapCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
int rc = 0, flag = 0;
char *retbuf = NULL;
if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Missing argument", false);
return 0;
}
if (!strcmp(argv[1], "startap")) {
rc = sSoftapCtrl->startSoftap();
} else if (!strcmp(argv[1], "stopap")) {
rc = sSoftapCtrl->stopSoftap();
} else if (!strcmp(argv[1], "fwreload")) {
rc = sSoftapCtrl->fwReloadSoftap(argc, argv);
} else if (!strcmp(argv[1], "clients")) {
rc = sSoftapCtrl->clientsSoftap(&retbuf);
if (!rc) {
cli->sendMsg(ResponseCode::CommandOkay, retbuf, false);
free(retbuf);
return 0;
}
} else if (!strcmp(argv[1], "status")) {
asprintf(&retbuf, "Softap service %s",
(sSoftapCtrl->isSoftapStarted() ? "started" : "stopped"));
cli->sendMsg(ResponseCode::SoftapStatusResult, retbuf, false);
free(retbuf);
return 0;
} else if (!strcmp(argv[1], "set")) {
rc = sSoftapCtrl->setSoftap(argc, argv);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Unknown cmd", false);
return 0;
}
if (!rc) {
cli->sendMsg(ResponseCode::CommandOkay, "Softap operation succeeded", false);
} else {
cli->sendMsg(ResponseCode::OperationFailed, "Softap operation failed", true);
}
return 0;
}
首先是"set“ 命令, 调用到c = sSoftapCtrl->setSoftap(argc, argv); 来配置网络
配置即将所有上层的网络设置写到HOSTAPD_CONF_FILE[] = "/data/misc/wifi/hostapd.conf" 中
("system/netd/SoftapController.cpp")
/*
* Arguments:
* argv[2] - wlan interface
* argv[3] - SSID
* argv[4] - Security
* argv[5] - Key
* argv[6] - Channel
* argv[7] - Preamble
* argv[8] - Max SCB
*/
int SoftapController::setSoftap(int argc, char *argv[]) {
char psk_str[2*SHA256_DIGEST_LENGTH+1];
int ret = 0, i = 0, fd;
char *ssid, *iface;
/* ..... */
iface = argv[2];
char *wbuf = NULL;
char *fbuf = NULL;
if (argc > 3) {
ssid = argv[3];
} else {
ssid = (char *)"AndroidAP";
}
if (argc > 4) {
if (!strcmp(argv[4], "wpa-psk")) {
generatePsk(ssid, argv[5], psk_str);
asprintf(&fbuf, "%swpa=1\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str);
} else if (!strcmp(argv[4], "wpa2-psk")) {
generatePsk(ssid, argv[5], psk_str);
asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str);
} else if (!strcmp(argv[4], "open")) {
asprintf(&fbuf, "%s", wbuf);
}
} else {
asprintf(&fbuf, "%s", wbuf);
}
fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660);
/*............*/
if (write(fd, fbuf, strlen(fbuf)) < 0) {
ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
ret = -1;
}
free(wbuf);
free(fbuf);
/* Note: apparently open can fail to set permissions correctly at times */
// .......
}
然后是"startap"命令调用rc = sSoftapCtrl->startSoftap(); 真正开启Softap
int SoftapController::startSoftap() {
pid_t pid = 1;
int ret = 0;
if (mPid) {
ALOGE("Softap already started");
return 0;
}
if (mSock < 0) {
ALOGE("Softap startap - failed to open socket");
return -1;
}
if ((pid = fork()) < 0) {
ALOGE("fork failed (%s)", strerror(errno));
return -1;
}
if (!pid) {
ensure_entropy_file_exists();
if (execl("/system/bin/hostapd", "/system/bin/hostapd",
"-e", WIFI_ENTROPY_FILE,
HOSTAPD_CONF_FILE, (char *) NULL)) {
ALOGE("execl failed (%s)", strerror(errno));
}
ALOGE("Should never get here!");
return -1;
} else {
mPid = pid;
ALOGD("Softap startap - Ok");
usleep(AP_BSS_START_DELAY);
}
return ret;
}
在startSoftap函数中调用了
execl("/system/bin/hostapd", "/system/bin/hostapd", "-e", WIFI_ENTROPY_FILE, HOSTAPD_CONF_FILE, (char *) NULL)
这里hostapd就是softap的deamon 程序 类似于wifi的的wpa_supplicant
至此所有wifi子系统从界面打开softap 到如何运行调用到deamon程序打开Softap的流程就是这样的
之后会介绍到Setting 界面"Portable Wi-Fi"的开启 以及 Hostapd 的一些东东