Un breve análisis de cómo monitorear la conexión de red wifi, la ejecución de dhcpcd y el control del suministro de energía a través de jni en Android
==================================================== ==================================================== =========
libs/android_runtime/android_net_wifi_Wifi.cpp
Parte de la interfaz jni
JNINativeMethod estático gWifiMethods[] = {
{ "loadDriver", "()Z", (void *)android_net_wifi_loadDriver },
{ "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },//Administración de energía
{ "connectToSupplicant", "()Z", (void *)android_net_wifi_connectToSupplicant },
{ "waitForEvent", "()Ljava/lang/String;", (void*) android_net_wifi_waitForEvent },
{ "disconnectCommand", "()Z", (void *)android_net_wifi_disconnectCommand },
...
};
int registro_android_net_wifi_WifiManager(JNIEnv* entorno)
{
...
devolver AndroidRuntime::registerNativeMethods(env,
WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));//Registrar jni
}
libs/android_runtime/AndroidRuntime.cpp
constante estática RegJNIRec gRegJNI[] = {
...
REG_JNI(register_android_net_wifi_WifiManager),
...
};
int AndroidRuntime::startReg(JNIEnv* entorno)
{
...
registrar_jni_procs(gRegJNI, NELEM(gRegJNI), env);
...
}
Tiempo de ejecución de Android::iniciar
=>startReg(env) llama al método int AndroidRuntime::startReg(JNIEnv* env)
==================================================== ==================================================== =========
controlador_carga_wifi
wifi_start_supplicant
=>asegurar_config_file_exists
//Compruebe si el archivo /data/misc/wifi/wpa_supplicant.conf existe. Si no existe, copie dinámicamente una copia de /system/etc/wifi/wpa_supplicant.conf.
android_net_wifi_connectToSupplicant
=>conexión_wifi_al_solicitante
=>
ctrl_conn = wpa_ctrl_open(ifnombre);
monitor_conn = wpa_ctrl_open(ifnombre);
wpa_ctrl_attach(monitor_conn);
android_net_wifi_waitForEvent
=>wifi_espera_para_evento
=>wpa_ctrl_recv(monitor_conn, buf, &nread);
=>recv(ctrl->s, respuesta, *reply_len, 0);// Bloqueando y esperando a que lleguen los datos de enlace de red de wpa_supplicant
=>Si en el área de datos buf recibidos, buf[0] es '<', significa que hay información de nivel, por lo que los datos '<'...'>' se eliminan y luego la función wifi_wait_for_event devuelve [luther. gliethttp].
java/android/android/net/wifi/WifiMonitor.java
clase pública WifiMonitor {
...
monitoreo de inicio público vacío() {
new MonitorThread().start();//Iniciar hilo de Java
}
clase MonitorThread extiende Thread {
MonitorThread público() {
super("Monitor Wifi");
}
ejecución pública vacía() {
para (;;) {
sureSupplicantConnection();//=>WifiNative.connectToSupplicant llama a la función jni android_net_wifi_connectToSupplicant
String eventStr = WifiNative.waitForEvent();//=>Llamar a la función jni android_net_wifi_waitForEvent
//privado estático final int CONECTADO = 1;
//privado estático final int DESCONECTADO = 2;
//cadena final estática privada eventPrefix = "CTRL-EVENT-";
//privado estático final int eventPrefixLen = eventPrefix.length();
//cadena final estática privada conectadoEvento = "CONECTADO";
//cadena final estática privada desconectadoEvent = "DESCONECTADO";
String eventName = eventStr.substring(eventPrefixLen);//Eliminar la cadena "CTRL-EVENT-"
int nameEnd = eventName.indexOf(' ');// Encuentra la posición del espacio posterior, que es cuando se envía wpa_supplicant
//#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED" tiene espacios integrados.
si (nombreEnd! = -1)
nombre del evento = nombre del evento.substring(0, fin del nombre);
evento internacional;
if (eventName.equals(connectedEvent))//Detecta el tipo de acción de cadena desde netlink
evento = CONECTADO;
de lo contrario si (nombre del evento.equals (evento desconectado))
evento = DESCONECTADO;
...
int ind = eventStr.indexOf(" - ");//CTRL-EVENT-CONNECTED - Conexión a...
si (ind! = -1)
eventData = eventStr.substring(ind + 3);
//Elimina los caracteres de control iniciales, usa la cadena de descripción después de "-" como datos reales y continúa procesando
...
si (evento == STATE_CHANGE) {
handleSupplicantStateChange(eventData);
} más si (evento == ESTADO_CONDUCTOR) {
handleDriverEvent(eventoDatos);
} demás {
handleEvent(event, eventData);// Para eventos de enlace de red como CONNECTED y DISCONNECTED, esta operación se realizará para manejar [luther.gliethttp]
// Si el solicitante ya no está, sal del hilo
si (evento == TERMINANDO) {
romper;
}
}
...
void handleEvent (evento int, resto de cadena) {
cambiar (evento) {
caso DESCONECTADO:
handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, resto);
romper;
caso CONECTADO:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, resto);//Visualización de la interfaz de control
romper;
...
}
la clase pública WifiStateTracker extiende NetworkStateTracker {
...
inicio vacío públicoEventLoop() {
mWifiMonitor.startMonitoring();//Inicia el hilo MonitorThread anterior
}
...
}
java/services/com/android/server/WifiService.java
WifiService de clase pública extiende IWifiManager.Stub {
...
setWifiEnabledBlocking booleano privado (habilitación booleana) {
final int eventualWifiState = habilitar WIFI_STATE_ENABLED: WIFI_STATE_DISABLED;
...
si (habilitar) {
si (WifiNative.loadDriver()) {
Log.e(TAG, "Error al cargar el controlador de Wi-Fi.");
actualizarWifiState(WIFI_STATE_UNKNOWN);
devolver falso;
}
si (WifiNative.startSupplicant()) {
WifiNative.unloadDriver();
Log.e(TAG, "Error al iniciar el demonio suplicante.");
actualizarWifiState(WIFI_STATE_UNKNOWN);
devolver falso;
}
mWifiStateTracker.startEventLoop();
// Inicie el hilo MonitorThread, espere a que wpa_supplicant reenvíe los datos del enlace de red y luego afecte aún más la visualización de la interfaz de acuerdo con el tipo de acción del enlace de red [luther.gliethttp].
}
...
}
java/android/android/net/wifi/WifiStateTracker.java
Gestión de energía
handleConnectedState vacío privado() {
...
mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget() // Pasa al método handleMessage a continuación.
...
}
vacío público en cambio (cambio automático booleano) {
...
manejarEstadoConectado();
...
}
la clase pública WifiStateTracker extiende NetworkStateTracker {
...
mensaje de mango público vacío (mensaje de mensaje) {
cambiar (msg.what) {
caso EVENT_SUPPLICANT_CONNECTION:
caso EVENT_NETWORK_STATE_CHANGED:
handleConnectedState();//llamar
...
clase privada DhcpHandler extiende Handler {
Controlador privado mTarget;
public DhcpHandler (Looper looper, objetivo del controlador) {
súper(bucleador);
mObjetivo = objetivo;
}
mensaje de mango público vacío (mensaje de mensaje) {
evento internacional;
//privado estático final int DRIVER_POWER_MODE_AUTO = 0;
//privado estático final int DRIVER_POWER_MODE_ACTIVE = 1;
cambiar (msg.what) {
caso EVENT_DHCP_START:
sincronizado (esto) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);//Configura el modo de energía y llama a android_net_wifi_setPowerModeCommand
}
Log.d(TAG, "DhcpHandler: solicitud DHCP iniciada");
//libs/android_runtime/android_net_NetUtils.cpp
// método JNINativo estático gNetworkUtilMethods[] = {
//{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z", (void *)android_net_utils_runDhcp },
//...
//};
if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {//Ejecutar la aplicación dhcp para la operación de la dirección IP
evento = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: solicitud DHCP exitosa");
} demás {
evento = EVENT_INTERFACE_CONFIGURATION_FAILED;
Log.i(TAG, "DhcpHandler: Error en la solicitud DHCP: " +
NetworkUtils.getDhcpError());
// Si dhcpcd no puede asignar IP, se ejecutará Message.obtain(mTarget, event).sendToTarget();
//WifiNative.disconnectCommand(); es decir: JNINativeMethod estático gWifiMethods[] = {
//android_net_wifi_disconnectCommand envía la cadena "DESCONECTAR" [luther.gliethttp]
// Luego ejecuta wpa_supplicant_ctrl_iface_process en el servidor wpa_supplicant
//wpa_supplicant_disassociate
}
sincronizado (esto) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO);
}
Mensaje.obtain(mTarget, evento).sendToTarget();
romper;
}
}
}
...
/**
* Enviar al rastreador una notificación de que hay una conexión con el solicitante
* el demonio ha sido establecido.
*/
//En la clase pública anterior WifiMonitor=>ensureSupplicantConnection
//=>
//mientras (!supplicantConnected) {
// booleano conectado;
//sincronizado (mWifiStateTracker) {
//connected = WifiNative.connectToSupplicant();//Si la conexión no tiene éxito, entonces el bucle while lo intenta hasta que el intento sea exitoso, o se define oneShot, solo un intento
//=>mWifiStateTracker.notifySupplicantConnection();//Si WifiNative.connectToSupplicant() tiene éxito, se ejecutará
// Llamada de mWifiStateTracker.notifySupplicantConnection();.
void notifySupplicantConnection() {//Enviar mensaje al objeto
Message.obtain(this, EVENT_SUPPLICANT_CONNECTION).sendToTarget();
}
void notifyStateChange(SupplicantState nuevoEstado) {
Message.obtain(this, EVENT_SUPPLICANT_STATE_CHANGED, newState).sendToTarget();
}
...
}
jboolean estático android_net_wifi_setPowerModeCommand (JNIEnv* env, jobject clazz, modo jint)
{
char cmdstr[256];
sprintf(cmdstr, "DRIVER POWERMODE %d", modo);
devolver doBooleanCommand(cmdstr, "OK");
}
android_net_wifi_setPowerModeCommand
=>hacer comando booleano
=>hacercomando
=>comando_wifi
=>wifi_send_command
=>wpa_ctrl_request
=>enviar a wpa_supplicant
Luego, wpa_supplicant realizará las siguientes operaciones de recepción:
sistema/extra/wpa_supplicant/main.c
=>wpa_supplicant_add_iface
=>wpa_supplicant_init_iface2
=>wpa_supplicant_ctrl_iface_init
=>Registrar las funciones de procesamiento del puerto de control ctrl_conn y del puerto de escucha monitor_conn
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);//función de procesamiento del controlador del puerto ctrl_conn
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);// Función de procesamiento de devolución de llamada del puerto monitor_conn, procesa datos de enlace de red a todos los puertos de escucha monitor_conn
=>wpa_supplicant_ctrl_iface_receive//Para método de comunicación Unix
=>wpa_supplicant_ctrl_iface_proceso
=>Si wpa_cli envía un comando en forma de controlador wpa_cli xxx, entonces llame a esta función
if (os_strncmp(buf, "DRIVER", 7) == 0) {// Omita los primeros 7 y pase el comando directamente
respuesta_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, respuesta, tamaño_respuesta);
=>wpa_supplicant_driver_cmd
=>wpa_drv_driver_cmd
=> Personalice la función de procesamiento de la extensión DRIVER, de modo que para el comando de administración de energía pasado por Java, wpa_drv_driver_cmd recibirá la cadena "POWERMODE 0" o "POWERMODE 1" [luther.gliethttp]
==================================================== ==================================================== =========
jni
=>ejecutarDhcp
=>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";
carácter constante estático DHCP_PROP_NAME_PREFIX[] = "dhcp";
const char *ctrl_prop = "ctl.start";
const char *desired_status = "en ejecución";
snprintf(result_prop_name, tamaño de(result_prop_name), "%s.%s.result",
DHCP_PROP_NAME_PREFIX,
interfaz);
property_set(result_prop_name, "");//Establecer dhcp.eth0.result=""; Espere hasta que dhcp se complete correctamente,
property_set(ctrl_prop, DAEMON_NAME);//Envía la palabra de comando de inicio "ctrl.start" al servicio llamado dhcpcd, que se encuentra en init.rc
// palabra de comando del proceso de servicio dhcpcd en init.rc
//servicio dhcpcd /system/bin/dhcpcd eth0
// desactivado
// un trago
wait_for_property(DAEMON_PROP_NAME, estado_deseado, 10);
//init.c=>proceso de inicio
//=>handle_property_set_fd es la palabra de comando "ctrl.start", por lo que se llama a handle_control_message para procesar el mensaje de control.
//=>handle_control_message
//=>msg_start
//=>
// servicio de estructura *svc = service_find_by_name(nombre);
//service_start(svc);//Iniciar svc, es decir, ejecutar: /system/bin/dhcpcd eth0
//=>servicio_inicio
//=>pid = bifurcación();
// if(pid == 0)execve(svc->args[0], (char**) svc->args, (char**) ENV); el proceso hijo ejecuta execve y ejecuta /system/bin/dhcpcd); , parámetros para eth0
//=>De lo contrario, el proceso padre, es decir, el proceso de inicio
//=>notify_service_state(svc->name, "running"); Establece la propiedad de estado del svc.
// snprintf(pname, sizeof(pname), "init.svc.%s", nombre);
// property_set(pname, state);// Entonces, de esta manera, wait_for_property(DAEMON_PROP_NAME, wanted_status, 10) puede pasar [luther.gliethttp] normalmente.
wait_for_property(result_prop_name, NULL, 15);//Esperando dhcp.eth0.result=non-null
==================================================== ==================================================== =========
sistema/extra/dhcpcd-4.0.0-beta9/dhcpcd.c
dhcpcd
=>principal
#define SYSCONFDIR "/system/etc/dhcpcd"
#definir PAQUETE "dhcpcd"
# define CONFIG SYSCONFDIR "/" PAQUETE ".conf"
# definir LIBEXECDIR "/system/etc/dhcpcd"
# definir SCRIPT LIBEXECDIR "/" PAQUETE "-run-hooks"
=>strlcpy(opciones->script, SCRIPT, sizeof(opciones->script));//Opciones predeterminadas->script="/system/etc/dhcpcd/dhcpcd-run-hooks"
=>f = fopen(cf ? cf : CONFIG, "r");//Si no se especifica ningún archivo .conf, utilice el archivo .conf predeterminado
=>parse_config_line//Analizar el archivo de configuración predeterminado "/system/etc/dhcpcd/dhcpcd.conf"
=>opción de análisis
=>Si hay una sección "script" en "/system/etc/dhcpcd/dhcpcd.conf"
=>Luego ejecute strlcpy(opciones->script, oarg, sizeof(opciones->script));
/*
{"script", argumento_requerido, NULL, 'c'},
{"opción", argumento_requerido, NULL, 'o'},
Parte del contenido de "/system/etc/dhcpcd/dhcpcd.conf" es el siguiente:
...
opción nombre_dominio_servidores, nombre_dominio, búsqueda_dominio, nombre_host
...
*/
=>dhcp_run
=>handle_dhcp_packet
=>manejar_dhcp
=>bind_dhcp
motivo = "TIEMPO DE ESPERA"; motivo = "BOUND"; motivo = "REBIND"; motivo = "RENEW";
sistema/extra/dhcpcd-4.0.0-beta9/configure.c
=> configurar(iface, motivo, estado->nuevo, estado->antiguo, &estado->arrendamiento, opciones, 1);
// Si dhcp se agota o tiene éxito, se llamará a exec_script para ejecutar el script.
//Ejecutar setprop dhcp.${interface}.resultado "fallido" o
//Ejecutar setprop dhcp.${interfaz}.resultado "ok"
=>exec_script(opciones, iface->nombre, motivo, NULL, antiguo);
=>Luego configure_env pasa el motivo al script a través de la variable de entorno
int exec_script(const struct options *opciones, const char *iface, const char *razón,
estructura const dhcp_message *dhcpn, estructura const dhcp_message *dhcpo)
=>pid = bifurcación();
=>if(pid == 0)execve(options->script, argv, env);//El proceso hijo ejecuta el script, el valor predeterminado es "/system/etc/dhcpcd/dhcpcd-run-hooks"
//El script dhcpcd-run-hooks decidirá si ejecutar los archivos correspondientes en el directorio system/etc/dhcpcd/dhcpcd-hook/* según el valor del nivel.
//Nuestro sistema tiene los siguientes 3 archivos en el directorio system/etc/dhcpcd/dhcpcd-hook/*
//95-configurado
//20-dns.conf
//01-prueba
=>El proceso principal regresa while (waitpid(pid, &status, 0) == -1) y espera a que se complete la ejecución del script del proceso secundario
sistema/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/20-dns.conf
sistema/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/95-configurado
...
setprop dhcp.${interfaz}.ipaddress "${nueva_dirección_ip}"
setprop dhcp.${interface}.result "ok"//Establece el atributo en ok
setprop dhcp.${interfaz}.resultado "fallido"
...
==================================================== ==================================================== =========
inet_init, tcp_prot
calcetín->ops->sendmsg(iocb, calcetín, mensaje, tamaño);
=>inetsw_array[]
=>inet_stream_ops
=>tcp_sendmsg
==================================================== ==================================================== =========
wpa_cli.c
=>principal
=>wpa_cli_interactivo
=>wpa_cli_recv_pending(monitor_conn, 0, 0);//Bloqueando y esperando a que wpa_supplicant envíe datos
=>Si action_monitor es verdadero, se realizarán algunas operaciones de procesamiento simples; de lo contrario, los datos enviados por wpa_supplicant se imprimirán directamente en la consola [luther.gliethttp].