Softap字面意思是用軟體實現AP的功能,讓你的行動裝置可以作為一個路由,讓別的站台連結。
事實上市需要硬體以及驅動的支援才能真正的實現這個功能。
第一節 Softap開啟流程。
在Android系統的Setting介面的wireless配置項中會看到一個“Portable Wi-Fi hotspot” 跟一個"Configure Wi-Fi hotspot setting"選項,可以進入系統配置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); } } }
首先檢測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的一些東東