Softap字面意思是用軟件實現AP的功能,讓你的移動設備能夠做爲一個路由,讓別的站點連接。好比讓別人的手機連上你的已經打開AP功能的手機,玩聯機遊戲或者上網等等 java
但事實上此功能是須要硬件以及驅動的支持才能真正的實現的。 android
Softap打開流程。 c++
在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狀態改變信息 app
- 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); less
- 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 用來檢測是否須要進行一些準備工做 socket
若是無需準備工做則執行startTethering 大戲即將上演了 期待ing
ide
- 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();
- }
- }
一、下載AP對應的 firmware
wifiFirmwareReload(wlanIface, "AP");
二、設置ap的ssid 加密方式 以及密碼
mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, getSecurityType(wifiConfig), wifiConfig.preSharedKey);
三、運行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 的一些東東