반응형
Notice
Recent Posts
Recent Comments
관리 메뉴

꿈꾸는 사람.

[Lollipop][Wifi] SoftAp 시작. sequence flow 분석 본문

IT/Android

[Lollipop][Wifi] SoftAp 시작. sequence flow 분석

현무랑 니니 2015. 9. 7. 02:49
반응형


code analysis to start SoftAP

1. TetherSettings

위치: packages/apps/Settings/src/com/android/settings/TetherSettings.java

사용자가 UI에서 Tethering을 켜서 SoftAp를 시작하는 과정이다.

startTethering()

private void startTethering() { switch (mTetherChoice) { case WIFI_TETHERING: mWifiApEnabler.setSoftapEnabled(true); (1) break; case BLUETOOTH_TETHERING: // turn on Bluetooth first break; case USB_TETHERING: setUsbTethering(true); break; default: //should not happen break; } }

(1) Tethering으로 Wi-Fi를 선택하면 WifiApEnabler.java의 setSoftapEnabled를 호출한다.



setSoftapEnabled()

위치: packages/apps/Settings/src/com/android/settings/wifi/WifiApEnabler.java

public void setSoftapEnabled(boolean enable) { ... if (mWifiManager.setWifiApEnabled(null, enable)) { (1) /* Disable here, enabled on receiving success broadcast */ mSwitch.setEnabled(false); } else { mSwitch.setSummary(R.string.wifi_error); } ... }

(1) WifiManager -> WifiService를 통해 CMD_LOAD_DRIVER과 CMD_START_AP 명령이 WifiStateMachine으로 전달된다.

CMD_START_AP의 전달과정을 분석해 보자.


2. WifiStateMachine

위치: frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

InitialState

UI 설정에 따라 SoftAp를 시작을 처리하는 상태 클래스이다.

CMD_START_AP 메시지를 processMessage()함수에서 아래처럼 처리한다.

public boolean processMessage(Message message) { switch(message.what) { case CMD_START_AP: if (mWifiNative.loadDriver()) { (1) setWifiApState(WIFI_AP_STATE_ENABLING); (2) transitionTo(mSoftApStartingState); (3) }

(1) mWifiNative.loadDriver()는 JNI를 통해 /hardware/libhardware_legacy/wifi/wifi.c 파일의 wifi_load_driver() 함수를 호출한다.

CMD_START_SUPPLICANT 메시지는 STA용 드라이버를 호출하지만 CMD_START_AP 메시지는 SoftAp 기능을 수행할 Wifi kernel driver를 호출한다.

(2) 정상적으로 드라이버가 호출되면 AP 상태를 WIFI_AP_STATE_ENABLING, 즉 활성화 중인 상태로 변경한다.

(3) SoftApStartingState 상태로 천이한다.


SoftApStartingState

상태 천이 후 enter()에서 아래처럼 CMD_START_AP 메시지를 처리한다.

class SoftApStartingState extends State { public void enter() { if (message.what == CMD_START_AP) { final WifiConfiguration config = (WifiConfiguration) message.obj; (1) if (config == null) { mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG); (2) } else { mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config); startSoftApWithConfig(config); (3) } } else { throw new RuntimeException("Illegal transition to SoftApStartingState: " + message); } }

(1) 보안을 포함한 Wi-Fi 네트웍의 구성 정보를 WifiConfiguration 클래스 객체인 config에 지정한다.

(2) config가 null이면 CMD_REQUEST_AP_CONFIG 메시지를 보내 AP 구성 정보를 요청한다.

(3) startSoftApWithConfig()를 호출하여 AP를 구동한다.


startSoftApWithConfig

SoftAp 기능은 external/wpa_supplicant_8 아래의 hostapd이 수행하는데 아래에서 독립된 스레드로 시작한다.

private void startSoftApWithConfig(final WifiConfiguration config) { // Start hostapd on a separate thread new Thread(new Runnable() { (1) public void run() { try { mNwService.startAccessPoint(config, mInterfaceName); (2) } 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(); }

(1) 스레드로 생성하여 시작한다.

(2) INetworkManagementService.aidl을 통해서 startAccessPoint()함수를 호출하여 AP를 시작한다.


3. INetworkManagementService

위치: frameworks/base/core/java/android/os/INetworkManagementService.aidl

AIDL로 NetworkManagementService.java에 연결된다.


4. NetworkManagementService

위치: frameworks/base/services/core/java/com/android/server/NetworkManagementService.java

startAccessPoint()

@Override public void startAccessPoint( WifiConfiguration wifiConfig, String wlanIface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { wifiFirmwareReload(wlanIface, "AP"); if (wifiConfig == null) { mConnector.execute("softap", "set", wlanIface); (1) } else { mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, "broadcast", "6", getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey)); } mConnector.execute("softap", "startap"); (2) } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } }

(1) netd을 통해 "softap set wlan0" 명령을 실행하도록 한다.

(2) netd을 통해 "softap startap" 명령을 실행하여 SoftAp가 구동하도록 한다. execute 함수는 명령을 netd의 CommandListener에 전달한다.


5. CommandListener

위치: system/netd/server/CommandListener.cpp

frameworks의 NetworkDamonConnector에 execute로 받은 softa의 commandline 명령을 처리한다.

CommandListener::SoftapCmd::runCommand()

int CommandListener::SoftapCmd::runCommand(SocketClient *cli, int argc, char **argv) { ... if (!strcmp(argv[1], "startap")) { (1) 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], "status")) { asprintf(&retbuf, "Softap service %s running", (sSoftapCtrl->isSoftapStarted() ? "is" : "is not")); cli->sendMsg(rc, retbuf, false); free(retbuf); return 0; } else if (!strcmp(argv[1], "set")) { rc = sSoftapCtrl->setSoftap(argc, argv); } else { cli->sendMsg(ResponseCode::CommandSyntaxError, "Unrecognized SoftAP command", false); return 0; } if (rc >= 400 && rc < 600) cli->sendMsg(rc, "SoftAP command has failed", false); else cli->sendMsg(rc, "Ok", false); return 0; }

(1) commandline으로 받은 "softap xxx" 명령을 처리할 SoftapController의 메소드를 호출한다.


6. SoftapController

위치: system/netd/server/SoftapController.cpp

SoftAp의 설정과 시작하는 함수를 분석해 보자.

SoftapController::setSoftap()

/* * Arguments: (1)  

* argv[2] - wlan interface 

* argv[3] - SSID  

* argv[4] - Broadcast/Hidden  

* argv[5] - Channel  

* argv[6] - Security  

* argv[7] - Key  

*/

int SoftapController::setSoftap(int argc, char *argv[]) {

... asprintf(&wbuf, "interface=%s\ndriver=nl80211\nctrl_interface=" (2) "/data/misc/wifi/hostapd\nssid=%s\nchannel=%d\nieee80211n=1\n" "hw_mode=g\nignore_broadcast_ssid=%d\nwowlan_triggers=any\n", argv[2], argv[3], channel, hidden);  

if (argc > 7) { if (!strcmp(argv[6], "wpa-psk")) { generatePsk(argv[3], argv[7], psk_str);  

asprintf(&fbuf, "%swpa=3\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str); (3)

} else if (!strcmp(argv[6], "wpa2-psk")) { generatePsk(argv[3], argv[7], psk_str); asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str); } else if (!strcmp(argv[6], "open")) { asprintf(&fbuf, "%s", wbuf);

} } ...

fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660); if (fd < 0) { ALOGE("Cannot update \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno)); free(wbuf); free(fbuf); return ResponseCode::OperationFailed; } if (write(fd, fbuf, strlen(fbuf)) < 0) { (4) ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno)); ret = ResponseCode::OperationFailed; } }

(1) command line으로 전달되는 명령의 인자들의 정의이다.

(2) 인자들을 wbuf에 지정된 형식으로 쓴다. 이 내용이 커널의 드라이버로 전달된다.

(3) 보안 관련 정보(wpa, psk key 등)를 추가한다.

(4) HOSTAPD_CONF_FILE 파일에 SoftAp 정보를 저장한 fbuf의 내용을 기록한다.


SoftapController::startSoftap()

이제 SoftAp를 시작하는 단계이다. 

static const char HOSTAPD_CONF_FILE[] = "/data/misc/wifi/hostapd.conf"; static const char HOSTAPD_BIN_FILE[] = "/system/bin/hostapd";

...

int SoftapController::startSoftap() { pid_t pid = 1; if (mPid) { ALOGE("SoftAP is already running"); return ResponseCode::SoftapStatusResult; } if ((pid = fork()) < 0) { (1) ALOGE("fork failed (%s)", strerror(errno)); return ResponseCode::ServiceStartFailed; } if (!pid) { ensure_entropy_file_exists(); if (execl(HOSTAPD_BIN_FILE, HOSTAPD_BIN_FILE, (2) "-e", WIFI_ENTROPY_FILE, HOSTAPD_CONF_FILE, (char *) NULL)) { ALOGE("execl failed (%s)", strerror(errno)); } ALOGE("SoftAP failed to start"); return ResponseCode::ServiceStartFailed; } else { mPid = pid; ALOGD("SoftAP started successfully"); usleep(AP_BSS_START_DELAY); } return ResponseCode::SoftapStatusResult; }

(1) SoftAp를 시작할 프로세스를 생성한다.

(2) 실패할 경우 /system/bin/hostapd 데몬을 실행하게 된다.


반응형
Comments