일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 단통법
- 조세피난처
- 타이젠
- 인공지능
- 리뷰
- Linux
- ubuntu 12.04
- 국정원
- ubuntu
- 해외직구
- 문파문파 공략
- 프로야구
- mysql
- 애플
- 우분투
- 문파문파
- 블로그
- python
- 디자인 패턴
- NC다이노스
- 태그를 입력해 주세요.
- 데이터베이스
- 손민한
- 야구
- Tizen
- 뉴스타파
- 안드로이드
- arm
- 김경문
- NC 다이노스
- Today
- Total
꿈꾸는 사람.
[Lollipop][Wifi] SoftAp 시작. sequence flow 분석 본문
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 데몬을 실행하게 된다.
'IT > Android' 카테고리의 다른 글
[Cordova] 하이브리드 앱 만들기 - 2 (0) | 2018.11.28 |
---|---|
[Android Studio] Failed to find Build Tools revision 28.0.2 해결 (6) | 2018.10.07 |
[G4] 비주얼 챌린저 모집. 화끈한 맞불. (0) | 2015.04.09 |
[갤럭시 S6 엣지 vs 아이폰 6+] 기대되는 진검 승부! (0) | 2015.03.31 |
[갤럭시6, 갤럭시6 에지] 삼성 언팩 2015. 총 정리. (0) | 2015.03.23 |