How to monitor Wi-Fi network connection, dhcpcd execution, and power supply control through JNI in Android
========================================================== ========================================================== ==================================
Libs/android_runtime/android_net_wifi_wifi.cpp
Some JNI Interfaces
Static jninativemethod gwifimethods [] = {
{"Loaddriver", "() Z", (void *) android_net_wifi_loaddriver },
{"Setpowermodecommand", "(I) Z", (void *) android_net_wifi_setpowermodecommand}, // Power Management
{"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 * env)
{
...
Return androidruntime: registernativemethods (ENV,
Wifi_pkg_name, gwifimethods, nelem (gwifimethods); // register JNI
}
Libs/android_runtime/androidruntime. cpp
Static const regjnirec gregjni [] = {
...
Reg_jni (register_android_net_wifi_wifimanager ),
...
};
Int androidruntime: starregulatory (jnienv * env)
{
...
Register_jni_procs (gregjni, nelem (gregjni), ENV );
...
}
Androidruntime: Start
=> Starregulatory (ENV): Call method intandroidruntime: starregulatory (jnienv * env)
========================================================== ========================================================== ==================================
Wifi_load_driver
Wifi_start_supplicant
=> Ensure_config_file_exists
// Check whether the/data/MISC/WiFi/wpa_supplicant.conf file exists. If not, dynamically copy one copy from/system/etc/WiFi/wpa_supplicant.conf.
Android_net_wifi_connecttosupplicant
=> 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); // block the Netlink data waiting for wpa_supplicant
=> If the received Buf data zone and Buf [0] is '<', the level information is displayed '... '>' remove the data, and then the wifi_wait_for_event function returns [Luther. gliethttp].
Java/Android/NET/WiFi/wifimonitor. Java
Public class wifimonitor {
...
Public void startmonitoring (){
New monitorthread (). Start (); // start the Java thread
}
Class monitorthread extends thread {
Public monitorthread (){
Super ("wifimonitor ");
}
Public void run (){
For (;;){
Ensuresupplicantconnecti
On (); // => wifinative. connecttosupplicant call the JNI function android_net_wifi_connecttosupplicant
String eventstr = wifinative. waitforevent (); // => call the JNI function android_net_wifi_waitforevent
// Private Static final int connected = 1;
// Private Static final int disconnected = 2;
// Private Static final string eventprefix = "CTRL-EVENT -";
// Private Static final int eventprefixlen = eventprefix. Length ();
// Private Static final string connectedevent = "connected ";
// Private Static final string disconnectedevent = "disconnected ";
String eventname = eventstr. substring (eventprefixlen); // remove "CTRL-EVENT-" String
Int nameend = eventname. indexof (''); // locate the subsequent space position, which is when wpa_supplicant sends
// # Define wpa_event_connected "CTRL-EVENT-CONNECTED", has built-in space.
If (nameend! =-1)
Eventname = eventname. substring (0, nameend );
Int event;
If (eventname. Equals (connectedevent) // detects the action type of the string from Netlink.
Event = connected;
Else if (eventname. Equals (disconnectedevent ))
Event = disconnected;
...
Int ind = eventstr. indexof ("-"); // CTRL-EVENT-CONNECTED-connection...
If (IND! =-1)
Eventdata = eventstr. substring (IND + 3 );
// Remove the leading control character and use the description string after "-" as the real data to continue processing
...
If (event = state_change ){
Handlesupplicantstatecha Nge (eventdata );
} Else if (event = driver_state ){
Handledriverevent (eventdata );
} Else {
Handleevent (event, eventdata); // this operation is performed on Netlink events such as connected and disconnected to process [Luther. gliethttp]
// If Supplicant is gone, exit the thread
If (event = terminating ){
Break;
}
}
...
Void handleevent (INT event, string remainder ){
Switch (event ){
Case disconnected:
Handlenetworkstatechange (Networkinfo. detailedstate. Disconnected, remainder );
Break;
Case connected:
Handlenetworkstatechange(Networkinfo. detailedstate. Connected, remainder); // control interface display
Break;
...
}
Public class wifistatetracker extends networkstatetracker {
...
Public void starteventloop (){
Mwifimonitor. startmonitoring (); // start the above monitorthread
}
...
}
Java/services/COM/Android/Server/wifiservice. Java
Public class wifiservice extends iwifimanager. Stub {
...
Private Boolean setwifienabledblocking (Boolean enable ){
Final int eventualwifistate = Enable? Wifi_state_enabled: wifi_state_disabled;
...
If (enable ){
If (wifinative. loaddriver ()){
Log. E (TAG, "failed to load wi-fi Driver .");
Updatewifistate (wifi_state_unknown );
Return false;
}
If (wifinative. startsupplicant ()){
Wifinative. unloaddriver ();
Log. E (TAG, "failed to start supplicant daemon .");
Updatewifistate (wifi_state_unknown );
Return false;
}
Mwifistatetracker. starteventloop ();
// Start the monitorthread and wait for wpa_supplicant to forward the Netlink data. Then, according to the Netlink action type, the interface displays [Luther. gliethttp].
}
...
}
Java/Android/NET/WiFi/wifistatetracker. Java
Power Management
Private void handleconnectedstate (){
...
Mdhcptarget. obtainmessage (event_dhcp_start). sendtotarget (); // The handlemessage method passed
...
}
Public void onchange (Boolean selfchange ){
...
Handleconnectedstate ();
...
}
Public class wifistatetracker extends networkstatetracker {
...
Public void handlemessage (Message MSG ){
Switch (msg. What ){
Case event_supplicant_connection:
Case event_network_state_changed:
Handleconnectedstate (); // call
...
Private class dhcphandler extends handler {
Private handler mtarget;
Public dhcphandler (Looper, Handler target ){
Super (logoff );
Mtarget = target;
}
Public void handlemessage (Message MSG ){
Int event;
// Private Static final int driver_power_mode_auto = 0;
// Private Static final int driver_power_mode_active = 1;
Switch (msg. What ){
Case event_dhcp_start:
Synchronized (this ){
Wifinative. setpowermodecommand (driver_power_mode_active); // sets the power mode and calls android_net_wifi_setpowermodecommand
}
Log. D (TAG, "dhcphandler: DHCP request started ");
// Libs/android_runtime/android_net_netutils.cpp
// Static jninativemethod gnetworkutilmethods [] = {
// {"Rundhcp", "(ljava/lang/string; landroid/NET/dhcpinfo;) Z", (void *) android_net_utils_rundhcp },
//...
//};
If (networkutils. rundhcp (minterfacename, mdhcpinfo) {// execute the DHCP request IP address operation
Event = event_interface_configuration_succeeded;
If (local_logd) log. V (TAG, "dhcphandler: DHCP requestsucceeded ");
} Else {
Event = event_interface_configuration_failed;
Log. I (TAG, "dhcphandler: DHCP request failed:" +
Networkutils. getdhcperror ());
// If IP Address Allocation fails for dhcpcd, message. Obtain (mtarget, event). sendtotarget ();
// Wifinative. disconnectcommand (); that is, static jninativemethowifimethods [] = {
// Android_net_wifi_disconnectcommand sends the "Disconnect" string [Luther. gliethttp]
// Then run wpa_supplicant on the wpa_supplicant_ctrl_iface_process server.
// Wpa_supplicant_disassociate
}
Synchronized (this ){
Wifinative. setpowermodecommand (driver_power_mode_auto );
}
Message. Obtain (mtarget, event). sendtotarget ();
Break;
}
}
}
...
// Public classwifimonitor => ensuresupplicantconnectiOn
// =>
// While (! Supplicantconnected ){
// Boolean connected;
// Synchronized (mwifistatetracker ){
// Connected = wifinative. connecttosupplicant (); // If the connection is not successful, the while loop attempts until the attempt is successful, or if oneshot is defined, only one attempt
// => Mwifistatetracker. yysupplicantconnectiOn (); // If wifinative. connecttosupplicant () is successful,
// Mwifistatetracker. yysupplicantconnectiOn.
Void yysupplicantconnectiOn () {// send a message to the object
Message. Obtain (this, event_supplicant_connection). sendtotarget ();
}
Void notifystatechange (supplicantstate newstate ){
Message. Obtain (this, event_supplicant_state_changed, newstate). sendtotarget ();
}
...
}
Static jboolean android_net_wifi_setpowermodecommand (jnienv * ENV, jobject clazz, jint Mode)
{
Char comment STR [256];
Sprintf (short STR, "Driver powermode % d", mode );
Return dobooleancommand (Response STR, "OK ");
}
Android_net_wifi_setpowermodecommand
=> Dobooleancommand
=> Docommand
=> Wifi_command
=> Wifi_send_command
=> Wpa_ctrl_request
=> Send to wpa_supplicant
Then wpa_supplicant will perform the following receiving operation:
System/extra/wpa_supplicant/Main. c
=> Wpa_supplicant_add_iface
=> Wpa_supplicant_init_iface2
=> Wpa_supplicant_ctrl_iface_init
=> Register the processing function of ctrl_conn control port and monitor_conn listening port
Eloop_register_read_sock (priv-> sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv); // handler processing function of ctrl_conn Port
Wpa_msg_register_cb (wpa_supplicant_ctrl_iface_msg_cb); // callback handler for the monitor_conn port to process Netlink data to all monitor_conn listening ports
=> Wpa_supplicant_ctrl_iface_receive // for Unix communication methods
=> Wpa_supplicant_ctrl_iface_process
=> If wpa_cli sends a command in the form of wpa_cli driverxxx, call this function.
If (OS _strncmp (BUF, "driver", 7) = 0) {// pass the first seven commands directly
Reply_len = wpa_supplicant_driver_cmd (wpa_s, BUF + 7, reply, reply_size );
=> Wpa_supplicant_driver_cmd
=> Wpa_drv_driver_cmd
=> Custom driver extended processing functions. Therefore, for the power management commands passed by Java, wpa_drv_driver_cmd receives the "powermode0" or "powermode 1" string [Luther. gliethttp]
========================================================== ========================================================== ==================================
JNI
=> 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 = "running ";
Snprintf (result_prop_name, sizeof (result_prop_name), "% S. % S. Result ",
Dhcp_prop_name_prefix,
Interface );
Property_set (result_prop_name, ""); // sets DHCP. eth0.result = ""; after DHCP is successfully completed,
Property_set (ctrl_prop, daemon_name); // send the "Ctrl. Start" Start command to the service named dhcpcd, which is in init. RC.
// Dhcpcd service process command in init. RC
// Service dhcpcd/system/bin/dhcpcd eth0
// Disabled
// Oneshot
Wait_for_property (daemon_prop_name, desired_status, 10 );
// Init. c => INIT process
// => Handle_property_set_fd is called to process control information because it is a "Ctrl. Start" command.
// => Handle_control_message
// => Msg_start
// =>
// Struct Service * SVC = service_find_by_name (name );
// Service_start (SVC); // start SVC, that is, run:/system/bin/dhcpcd eth0
// => Service_start
// => Pid = fork ();
// If (pid = 0) execve (SVC-> ARGs [0], (char **) SVC-> ARGs, (char **) ENV ); execve the sub-process to run/system/bin/dhcpcd. The parameter is eth0.
// => Otherwise, the parent process, that is, the INIT process will
// => Policy_service_state (SVC-> name, "running"); sets the status of the SVC prop
// Snprintf (pname, sizeof (pname), "init. SVC. % s", name );
// Property_set (pname, State); // in this case, wait_for_property (daemon_prop_name, desired_status, 10) above can pass [Luther. gliethttp] Normally.
Wait_for_property (result_prop_name, null, 15); // wait for DHCP. eth0.result = not empty
========================================================== ========================================================== ==================================
System/extra/dhcpcd-4.0.0-beta9/dhcpcd. c
Dhcpcd
=> Main
# Define sysconfdir "/system/etc/dhcpcd"
# Define package "dhcpcd"
# Define config sysconfdir "/" package ". conf"
# Define libexecdir "/system/etc/dhcpcd"
# Define script libexecdir "/" package "-run-hooks"
=> Strlcpy (options-> script, script, sizeof (options-> script )); // default options-> script = "/system/etc/dhcpcd-run-hooks"
=> F = fopen (CF? Cf: config, "R"); // If the. conf file is not specified, use the default. conf file.
=> Parse_config_line // parse "/system/etc/dhcpcd. conf" Default Configuration File
=> Parse_option
=> If there is a "script" section in "/system/etc/dhcpcd. conf ",
=> Then execute strlcpy (options-> script, oarg, sizeof (options-> script); directly copy
=> Dhcp_run
=> Handle_dhcp_packet
=> Handle_dhcp
=> Bind_dhcp
Reason = "timeout"; reason = "Bound"; reason = "rebind"; reason = "renew ";
System/extra/dhcpcd-4.0.0-beta9/configure. c
=> Configure (iface, reason, State-> new, state-> old, & State-> lease, options, 1 );
// If DHCP times out or DHCP succeeds, exec_script is called to execute the script,
// Execute setprop DHCP. $ {interface}. Result "failed" or
// Execute setprop DHCP. $ {interface}. Result "OK"
=> Exec_script (options, iface-> name, reason, null, old );
=> Configure_env then passes the reason to the script through the Environment Variable
Int exec_script (const struct options * options, const char * iface, const char * reason,
Const struct dhcp_message * dhcpn, const structdhcp_message * dhcpo)
=> Pid = fork ();
=> If (pid = 0) execve (options-> script, argv, ENV); // The sub-process executes the script, default Value: "/system/etc/dhcpcd-run-hooks"
// The dhcpcd-run-hooks script determines whether to execute the corresponding files in the system/etc/dhcpcd-hook/* directory based on the level value.
// Our system has the following three files in the system/etc/dhcpcd-hook/* directory:
// 95-configured
// 20-dns.conf
// 01-test
=> The parent process returns while (waitpid (PID, & status, 0) =-1) waiting for the sub-process script to be executed.
System/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/20-dns.conf
System/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/95-configured
...
Setprop DHCP. $ {interface}. IPaddress "$ {new_ip_address }"
Setprop DHCP. $ {interface}. Result "OK" // set the property to OK.
Setprop DHCP. $ {interface}. Result "failed"
...
========================================================== ========================================================== ==================================
Inet_init, tcp_prot
Sock-> OPS-> sendmsg (iocb, Sock, MSG, size );
=> Inetsw_array []
=> Inet_stream_ops
=> Tcp_sendmsg
========================================================== ========================================================== ==================================
Wpa_cli.c
=> Main
=> Wpa_cli_interactive
=> Wpa_cli_recv_pending (monitor_conn,); // blocking waits for wpa_supplicant to send data
=> If action_monitor is true, some simple processing operations will be performed. Otherwise, the data sent from wpa_supplicant will be printed directly to the console [Luther. gliethttp]