Android에서 jni를 통해 Wi-Fi 네트워크 연결, dhcpcd 실행 및 전원 공급 장치 제어를 모니터링하는 방법에 대한 간략한 분석
================================================= ================================================= =========
libs/android_runtime/android_net_wifi_Wifi.cpp
JNI 인터페이스의 일부
정적 JNINativeMethod gWifiMethods[] = {
{ "loadDriver", "()Z", (void *)android_net_wifi_loadDriver },
{ "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },//전원 관리
{ "connectToSupplicant", "()Z", (void *)android_net_wifi_connectToSupplicant },
{ "waitForEvent", "()Ljava/lang/String;", (void*) android_net_wifi_waitForEvent },
{ "disconnectCommand", "()Z", (void *)android_net_wifi_disconnectCommand },
...
};
int Register_android_net_wifi_WifiManager(JNIEnv* 환경)
{
...
AndroidRuntime::registerNativeMethods(env,
WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));//jni 등록
}
libs/android_runtime/AndroidRuntime.cpp
정적 const RegJNIRec gRegJNI[] = {
...
REG_JNI(register_android_net_wifi_WifiManager),
...
};
int AndroidRuntime::startReg(JNIEnv* env)
{
...
Register_jni_procs(gRegJNI, NELEM(gRegJNI), env);
...
}
Android런타임::시작
=>startReg(env)는 int AndroidRuntime::startReg(JNIEnv* env) 메서드를 호출합니다.
================================================= ================================================= =========
wifi_load_driver
wifi_start_신청자
=>ensure_config_file_exists
///data/misc/wifi/wpa_supplicant.conf 파일이 있는지 확인합니다. 파일이 없으면 /system/etc/wifi/wpa_supplicant.conf에서 복사본을 동적으로 복사합니다.
android_net_wifi_connectTo신청자
=>wifi_connect_to_supplicant
=>
ctrl_conn = wpa_ctrl_open(ifname);
monitor_conn = wpa_ctrl_open(ifname);
wpa_ctrl_attach(monitor_conn);
android_net_wifi_waitForEvent
=>wifi_wait_for_event
=>wpa_ctrl_recv(monitor_conn, buf, &nread);
=>recv(ctrl->s, reply, *reply_len, 0);//wpa_supplicant의 넷링크 데이터가 오기를 차단하고 기다리는 중
=>수신된 buf 데이터 영역에서 buf[0]이 '<'이면 레벨 정보가 있다는 의미이므로 '<'...'>' 데이터가 제거되고, wifi_wait_for_event 함수는 [luther. 글리트http].
자바/안드로이드/안드로이드/net/wifi/WifiMonitor.java
공개 클래스 WifiMonitor {
...
공공 무효 startMonitoring() {
new MonitorThread().start();//Java 스레드 시작
}
MonitorThread 클래스는 Thread {를 확장합니다.
공개 MonitorThread() {
super("와이파이모니터");
}
공개 무효 실행() {
을 위한 (;;) {
verifySupplicantConnection();//=>WifiNative.connectToSupplicant가 jni 함수 android_net_wifi_connectToSupplicant를 호출합니다.
String eventStr = WifiNative.waitForEvent();//=>jni 함수 호출 android_net_wifi_waitForEvent
//개인 정적 최종 int CONNECTED = 1;
//개인 정적 최종 int DISCONNECTED = 2;
//개인 정적 최종 문자열 eventPrefix = "CTRL-EVENT-";
//개인 정적 최종 int eventPrefixLen = eventPrefix.length();
//개인 정적 최종 문자열 연결 이벤트 = "연결됨";
//개인 정적 최종 문자열 연결 해제 이벤트 = "DISCONNECTED";
String eventName = eventStr.substring(eventPrefixLen);//"CTRL-EVENT-" 문자열 제거
int nameEnd = eventName.indexOf(' ');//wpa_supplicant가 전송되는 후속 공간 위치를 찾습니다.
//#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED"에는 공백이 내장되어 있습니다.
if (nameEnd != -1)
eventName = eventName.substring(0, nameEnd);
정수 이벤트;
if (eventName.equals(connectedEvent))//netlink에서 문자열 작업 유형 감지
이벤트 = 연결됨;
else if(eventName.equals(disconnectedEvent))
이벤트 = 연결이 끊어졌습니다.
...
int ind = eventStr.indexOf(" - ");//CTRL-EVENT-CONNECTED - 연결 ...
if (ind != -1)
eventData = eventStr.substring(ind + 3);
//선행 제어 문자를 제거하고 "-" 뒤의 설명 문자열을 실제 데이터로 사용하여 처리를 계속합니다.
...
if (이벤트 == STATE_CHANGE) {
handlerSupplicantStateChange(eventData);
} else if (이벤트 == DRIVER_STATE) {
handlerDriverEvent(eventData);
} 또 다른 {
handlerEvent(event, eventData);//CONNECTED 및 DISCONNECTED와 같은 netlink 이벤트의 경우 이 작업은 [luther.gliethttp]를 처리하기 위해 수행됩니다.
// 신청자가 사라지면 스레드를 종료합니다.
if (이벤트 == 종료 중) {
부서지다;
}
}
...
void handlerEvent(int 이벤트, 문자열 나머지) {
스위치(이벤트) {
케이스 연결 해제됨:
handlerNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, 나머지);
부서지다;
연결된 케이스:
handlerNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, 나머지);//인터페이스 표시 제어
부서지다;
...
}
공개 클래스 WifiStateTracker는 NetworkStateTracker를 확장합니다.
...
공공 무효 startEventLoop() {
mWifiMonitor.startMonitoring();//위 MonitorThread 스레드 시작
}
...
}
자바/서비스/com/android/server/WifiService.java
공개 클래스 WifiService는 IWifiManager.Stub을 확장합니다.
...
개인 부울 setWifiEnabledBlocking(boolean 활성화) {
final int eventualWifiState = 활성화 ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
...
if (활성화) {
if (WifiNative.loadDriver()) {
Log.e(TAG, "Wi-Fi 드라이버를 로드하지 못했습니다.");
updateWifiState(WIFI_STATE_UNKNOWN);
거짓을 반환;
}
if (WifiNative.startSupplicant()) {
WifiNative.unloadDriver();
Log.e(TAG, "신청자 데몬을 시작하지 못했습니다.");
updateWifiState(WIFI_STATE_UNKNOWN);
거짓을 반환;
}
mWifiStateTracker.startEventLoop();
//MonitorThread 스레드를 시작하고 wpa_supplicant가 netlink 데이터를 전달할 때까지 기다린 다음 netlink 작업 유형 [luther.gliethttp]에 따라 인터페이스 표시에 추가 영향을 미칩니다.
}
...
}
자바/안드로이드/안드로이드/net/wifi/WifiStateTracker.java
전원 관리
개인 무효 handlerConnectedState() {
...
mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget() // 아래의 handlerMessage 메소드에 전달합니다.
...
}
공공 무효 onChange(부울 selfChange) {
...
핸들커넥티드상태();
...
}
공개 클래스 WifiStateTracker는 NetworkStateTracker를 확장합니다.
...
공개 무효 핸들 메시지(메시지 메시지) {
스위치(msg.what) {
사례 EVENT_SUPPLICANT_CONNECTION:
사례 EVENT_NETWORK_STATE_CHANGED:
handlerConnectedState();//호출
...
개인 클래스 DhcpHandler는 핸들러를 확장합니다.
개인 처리기 mTarget;
public DhcpHandler(루퍼 루퍼, 핸들러 대상) {
슈퍼(루퍼);
mTarget = 타겟;
}
공개 무효 핸들 메시지(메시지 메시지) {
정수 이벤트;
//개인 정적 최종 int DRIVER_POWER_MODE_AUTO = 0;
//개인 정적 최종 int DRIVER_POWER_MODE_ACTIVE = 1;
스위치(msg.what) {
사례 EVENT_DHCP_START:
동기화됨 (이것) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);//전원 모드를 설정하고 android_net_wifi_setPowerModeCommand를 호출합니다.
}
Log.d(TAG, "DhcpHandler: DHCP 요청이 시작되었습니다.");
//libs/android_runtime/android_net_NetUtils.cpp
//정적 JNINativeMethod gNetworkUtilMethods[] = {
//{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z", (void *)android_net_utils_runDhcp },
// ...
//};
if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {//IP 주소 작업을 위한 dhcp 애플리케이션 실행
이벤트 = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP 요청 성공");
} 또 다른 {
이벤트 = EVENT_INTERFACE_CONFIGURATION_FAILED;
Log.i(TAG, "DhcpHandler: DHCP 요청 실패: " +
NetworkUtils.getDhcpError());
//dhcpcd가 IP 할당에 실패하면 Message.obtain(mTarget, event).sendToTarget()이 실행됩니다.
//WifiNative.disconnectCommand(); 즉: static JNINativeMethod gWifiMethods[] = {
//android_net_wifi_disconnectCommand가 "DISCONNECT" 문자열을 보냅니다. [luther.gliethttp]
//그런 다음 wpa_supplicant 서버에서 wpa_supplicant_ctrl_iface_process를 실행합니다.
//wpa_supplicant_disassociate
}
동기화됨 (이것) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO);
}
Message.obtain(mTarget, event).sendToTarget();
부서지다;
}
}
}
...
/**
* 신청자에게 연결되었다는 알림을 추적자에게 보냅니다.
* 데몬이 설치되었습니다.
*/
//위의 공개 클래스에서 WifiMonitor=>ensureSupplicantConnection
//=>
//동안 (!supplicantConnected) {
// 부울 연결됨;
//동기화됨(mWifiStateTracker) {
//connected = WifiNative.connectToSupplicant();//연결이 성공하지 못한 경우 while 루프는 시도가 성공할 때까지 시도하거나 oneShot이 정의되면 한 번만 시도합니다.
//=>mWifiStateTracker.notifySupplicantConnection();//WifiNative.connectToSupplicant()가 성공하면 실행됩니다.
//mWifiStateTracker.notifySupplicantConnection() 호출;.
void informSupplicantConnection() {//객체에 메시지 보내기
Message.obtain(this, EVENT_SUPPLICANT_CONNECTION).sendToTarget();
}
void informStateChange(SupplicantState newState) {
Message.obtain(this, EVENT_SUPPLICANT_STATE_CHANGED, newState).sendToTarget();
}
...
}
static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, jint 모드)
{
숯 cmdstr[256];
sprintf(cmdstr, "DRIVER POWERMODE %d", 모드);
return doBooleanCommand(cmdstr, "OK");
}
android_net_wifi_setPowerMode명령
=>doBoolean명령
=>do명령
=>wifi_command
=>wifi_send_command
=>wpa_ctrl_request
=>wpa_supplicant로 보내기
그런 다음 wpa_supplicant는 다음 수신 작업을 수행합니다.
시스템/추가/wpa_supplicant/main.c
=>wpa_supplicant_add_iface
=>wpa_supplicant_init_iface2
=>wpa_supplicant_ctrl_iface_init
=>ctrl_conn 제어 포트와 monitor_conn 수신 포트의 처리 기능을 등록합니다.
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);//ctrl_conn 포트의 핸들러 처리 함수
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);//monitor_conn 포트의 콜백 처리 기능, 모든 monitor_conn 수신 포트에 대한 netlink 데이터 처리
=>wpa_supplicant_ctrl_iface_receive//유닉스 통신 방식의 경우
=>wpa_supplicant_ctrl_iface_process
=>wpa_cli가 wpa_cli 드라이버 xxx 형식으로 명령을 보내는 경우 이 함수를 호출합니다.
if (os_strncmp(buf, "DRIVER ", 7) == 0) {//처음 7을 건너뛰고 명령을 직접 전달합니다.
reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, 회신, reply_size);
=>wpa_supplicant_driver_cmd
=>wpa_drv_driver_cmd
=> DRIVER 확장 처리 기능을 사용자 정의하여 Java가 전달한 전원 관리 명령의 경우 wpa_drv_driver_cmd가 "POWERMODE 0" 또는 "POWERMODE 1" 문자열을 수신하도록 합니다. [luther.gliethttp]
================================================= ================================================= =========
지니
=>runDhcp
=>android_net_utils_runDhcp
libs/netutils/dhcp_utils.c
=>dhcp_do_request
=>
static const char DAEMON_NAME[] = "dhcpcd";
static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd";
static const char DHCP_PROP_NAME_PREFIX[] = "dhcp";
const char *ctrl_prop = "ctl.start";
const char *desired_status = "실행 중";
snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
DHCP_PROP_NAME_PREFIX,
인터페이스);
property_set(result_prop_name, "");//Set dhcp.eth0.result=""; dhcp가 성공적으로 완료될 때까지 기다리세요.
property_set(ctrl_prop, DAEMON_NAME);//init.rc에 있는 dhcpcd라는 서비스에 "ctrl.start" 시작 명령을 보냅니다.
//init.rc의 dhcpcd 서비스 프로세스 명령 단어
//서비스 dhcpcd /system/bin/dhcpcd eth0
// 장애가 있는
// 원샷
wait_for_property(DAEMON_PROP_NAME, Desired_status, 10);
//init.c=>초기화 프로세스
//=>handle_property_set_fd는 "ctrl.start" 명령어이므로 제어 메시지를 처리하기 위해 handler_control_message가 호출됩니다.
//=>handle_control_message
//=>msg_start
//=>
// 서비스 구조 *svc = service_find_by_name(name);
//service_start(svc);//svc 시작, 즉 실행: /system/bin/dhcpcd eth0
//=>service_start
//=>pid = 포크();
// if(pid == 0)execve(svc->args[0], (char**) svc->args, (char**) ENV); 하위 프로세스는 execve를 실행하고 /system/bin/dhcpcd를 실행합니다. , eth0에 대한 매개변수
//=>그렇지 않으면 상위 프로세스, 즉 init 프로세스가
//=>notify_service_state(svc->name, "running") svc의 상태 속성을 설정합니다.
// snprintf(pname, sizeof(pname), "init.svc.%s", name);
// property_set(pname, state);//이러한 방식으로 wait_for_property(DAEMON_PROP_NAME, Desired_status, 10);는 [luther.gliethttp]를 정상적으로 전달할 수 있습니다.
wait_for_property(result_prop_name, NULL, 15);//dhcp.eth0.result=non-null을 기다리는 중
================================================= ================================================= =========
시스템/추가/dhcpcd-4.0.0-beta9/dhcpcd.c
dhcpcd
=>메인
#define SYSCONFDIR "/system/etc/dhcpcd"
#define 패키지 "dhcpcd"
# CONFIG SYSCONFDIR "/" PACKAGE ".conf" 정의
# LIBEXECDIR "/system/etc/dhcpcd"를 정의합니다.
# SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks" 정의
=>strlcpy(옵션->스크립트, SCRIPT, sizeof(옵션->스크립트));//기본 옵션->script="/system/etc/dhcpcd/dhcpcd-run-hooks"
=>f = fopen(cf ? cf : CONFIG, "r");//.conf 파일이 지정되지 않은 경우 기본 .conf 파일을 사용합니다.
=>parse_config_line//"/system/etc/dhcpcd/dhcpcd.conf" 기본 구성 파일 구문 분석
=>parse_option
=>"/system/etc/dhcpcd/dhcpcd.conf"에 "script" 섹션이 있는 경우
=>그런 다음 strlcpy(options->script, oarg, sizeof(options->script))를 직접 실행합니다.
/*
{"스크립트", 필수_인수, NULL, 'c'},
{"옵션", 필수_인수, NULL, 'o'},
"/system/etc/dhcpcd/dhcpcd.conf"의 내용 중 일부는 다음과 같습니다.
...
옵션 domain_name_servers, domain_name, domain_search, 호스트_이름
...
*/
=>dhcp_run
=>handle_dhcp_packet
=>handle_dhcp
=>bind_dhcp
이유 = "시간 초과"; 이유 = "바운드"; 이유 = "재바인드"; 이유 = "갱신";
시스템/추가/dhcpcd-4.0.0-beta9/configure.c
=> 구성(iface, 이유, 상태->신규, 상태->이전, &상태->리스, 옵션, 1);
//dhcp 시간이 초과되거나 dhcp가 성공하면 exec_script가 호출되어 스크립트를 실행합니다.
//setprop dhcp.${interface}.result "실패" 실행 또는
//setprop dhcp.${interface}.result "ok" 실행
=>exec_script(옵션, iface->이름, 이유, NULL, 이전);
=>configure_env는 환경 변수를 통해 이유를 스크립트에 전달합니다.
int exec_script(const struct options *options, const char *iface, const char *reason,
const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
=>pid = 포크();
=>if(pid == 0)execve(options->script, argv, env);//자식 프로세스가 스크립트를 실행합니다. 기본값은 "/system/etc/dhcpcd/dhcpcd-run-hooks"입니다.
//dhcpcd-run-hooks 스크립트는 level 값에 따라 system/etc/dhcpcd/dhcpcd-hook/* 디렉터리의 해당 파일을 실행할지 여부를 결정합니다.
//우리 시스템에는 system/etc/dhcpcd/dhcpcd-hook/* 디렉토리에 다음 3개의 파일이 있습니다.
//95로 구성됨
//20-dns.conf
//01-테스트
=>상위 프로세스는 while (waitpid(pid, &status, 0) == -1)을 반환하고 하위 프로세스 스크립트 실행이 완료될 때까지 기다립니다.
시스템/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/20-dns.conf
시스템/추가/dhcpcd-4.0.0-beta9/dhcpcd-hooks/95-configured
...
setprop dhcp.${인터페이스}.ipaddress "${new_ip_address}"
setprop dhcp.${interface}.result "ok"//속성을 ok로 설정
setprop dhcp.${interface}.result "실패"
...
================================================= ================================================= =========
inet_init, tcp_prot
양말->ops->sendmsg(iocb, 양말, 메시지, 크기);
=>inetsw_배열[]
=>inet_stream_ops
=>tcp_sendmsg
================================================= ================================================= =========
wpa_cli.c
=>메인
=>wpa_cli_interactive
=>wpa_cli_recv_pending(monitor_conn, 0, 0);//wpa_supplicant가 데이터를 보내는 것을 차단하고 기다리는 중
=>action_monitor가 true이면 몇 가지 간단한 처리 작업이 수행되고, 그렇지 않으면 wpa_supplicant가 보낸 데이터가 콘솔 [luther.gliethttp]에 직접 인쇄됩니다.