Une brève analyse sur la façon de surveiller la connexion réseau wifi, l'exécution de dhcpcd et le contrôle de l'alimentation via jni sous Android
=================================================== =================================================== =========
libs/android_runtime/android_net_wifi_Wifi.cpp
Une partie de l'interface jni
statique JNINativeMethod gWifiMethods[] = {
{ "loadDriver", "()Z", (void *)android_net_wifi_loadDriver },
{ "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },//Gestion de l'alimentation
{ "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));//Enregistrer jni
}
libs/android_runtime/AndroidRuntime.cpp
const statique RegJNIRec gRegJNI[] = {
...
REG_JNI(register_android_net_wifi_WifiManager),
...
} ;
int AndroidRuntime :: startReg (JNIEnv* env)
{
...
register_jni_procs(gRegJNI, NELEM(gRegJNI), env);
...
}
AndroidRuntime :: démarrer
=>startReg(env) appelle la méthode int AndroidRuntime::startReg(JNIEnv* env)
=================================================== =================================================== =========
wifi_load_driver
wifi_start_supplicant
=>ensure_config_file_exists
// Vérifiez si le fichier /data/misc/wifi/wpa_supplicant.conf existe. S'il n'existe pas, copiez dynamiquement une copie depuis /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, réponse, *reply_len, 0);//Blocage et attente des données netlink de wpa_supplicant
=>Si dans la zone de données buf reçue, buf[0] est '<', cela signifie qu'il y a des informations de niveau, donc les données '<'...'>' sont supprimées, puis la fonction wifi_wait_for_event renvoie [luther. gliethttp].
java/android/android/net/wifi/WifiMonitor.java
classe publique WifiMonitor {
...
public void startMonitoring() {
new MonitorThread().start();//Démarrer le fil Java
}
la classe MonitorThread étend le fil {
public MonitorThread() {
super("WifiMoniteur");
}
public void run() {
pour (;;) {
EnsureSupplicantConnection();//=>WifiNative.connectToSupplicant appelle la fonction jni android_net_wifi_connectToSupplicant
String eventStr = WifiNative.waitForEvent();//=>Appeler la fonction jni android_net_wifi_waitForEvent
// final statique privé int CONNECTED = 1;
// final statique privé int DISCONNECTED = 2;
// Chaîne finale statique privée eventPrefix = "CTRL-EVENT-";
//privé statique final int eventPrefixLen = eventPrefix.length();
// Chaîne finale statique privéeconnectedEvent = "CONNECTED";
// Chaîne finale statique privée déconnectéeEvent = "DISCONNECTED";
String eventName = eventStr.substring(eventPrefixLen);//Supprimer la chaîne "CTRL-EVENT-"
int nameEnd = eventName.indexOf(' ');//Trouver la position spatiale suivante, c'est-à-dire lorsque wpa_supplicant est envoyé
//#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED" a des espaces intégrés.
if (nomFin != -1)
eventName = eventName.substring(0, nameEnd);
événement int ;
if (eventName.equals(connectedEvent))//Détecter le type d'action de chaîne à partir de netlink
événement = CONNECTÉ ;
sinon si (eventName.equals (disconnectedEvent))
événement = DÉCONNECTÉ ;
...
int ind = eventStr.indexOf(" - ");//CTRL-EVENT-CONNECTED - Connexion à...
si (ind != -1)
eventData = eventStr.substring(ind + 3);
//Supprimez les premiers caractères de contrôle, utilisez la chaîne de description après "-" comme données réelles et continuez le traitement
...
si (événement == STATE_CHANGE) {
handleSupplicantStateChange(eventData);
} sinon si (événement == DRIVER_STATE) {
handleDriverEvent(eventData);
} autre {
handleEvent(event, eventData);//Pour les événements netlink tels que CONNECTED et DISCONNECTED, cette opération sera effectuée pour gérer [luther.gliethttp]
// Si le demandeur est parti, quittez le fil de discussion
if (événement == TERMINATION) {
casser;
}
}
...
void handleEvent (événement int, reste de chaîne) {
changer (événement) {
cas DÉCONNECTÉ :
handleNetworkStateChange (NetworkInfo.DetailedState.DISCONNECTED, reste);
casser;
cas CONNECTÉ :
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, rest);//Affichage de l'interface de contrôle
casser;
...
}
la classe publique WifiStateTracker étend NetworkStateTracker {
...
public void startEventLoop() {
mWifiMonitor.startMonitoring();//Démarrer le fil MonitorThread ci-dessus
}
...
}
java/services/com/android/server/WifiService.java
la classe publique WifiService étend IWifiManager.Stub {
...
private boolean setWifiEnabledBlocking (activation booléenne) {
final int eventualWifiState = activer WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
...
si (activer) {
si (WifiNative.loadDriver()) {
Log.e(TAG, "Échec du chargement du pilote Wi-Fi.");
updateWifiState(WIFI_STATE_UNKNOWN);
renvoie faux ;
}
si (WifiNative.startSupplicant()) {
WifiNative.unloadDriver();
Log.e(TAG, "Échec du démarrage du démon suppliant.");
updateWifiState(WIFI_STATE_UNKNOWN);
renvoie faux ;
}
mWifiStateTracker.startEventLoop();
// Démarrez le thread MonitorThread, attendez que wpa_supplicant transmette les données netlink, puis affectez davantage l'affichage de l'interface en fonction du type d'action netlink [luther.gliethttp].
}
...
}
java/android/android/net/wifi/WifiStateTracker.java
Gestion de l'alimentation
private void handleConnectedState() {
...
mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget(); // Passer à la méthode handleMessage ci-dessous
...
}
public void onChange (boolean selfChange) {
...
handleConnectedState();
...
}
la classe publique WifiStateTracker étend NetworkStateTracker {
...
public void handleMessage (Message msg) {
changer (msg.quoi) {
cas EVENT_SUPPLICANT_CONNECTION :
cas EVENT_NETWORK_STATE_CHANGED :
handleConnectedState();//appel
...
la classe privée DhcpHandler étend Handler {
Gestionnaire privé mTarget ;
public DhcpHandler (boucleur, cible du gestionnaire) {
super(boucleur);
mTarget = cible ;
}
public void handleMessage (Message msg) {
événement int ;
// final statique privé int DRIVER_POWER_MODE_AUTO = 0;
// final statique privé int DRIVER_POWER_MODE_ACTIVE = 1;
changer (msg.quoi) {
cas EVENT_DHCP_START :
synchronisé (ce) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);//Définissez le mode d'alimentation et appelez android_net_wifi_setPowerModeCommand
}
Log.d(TAG, "DhcpHandler : requête DHCP démarrée");
//libs/android_runtime/android_net_NetUtils.cpp
//statique JNINativeMethod gNetworkUtilMethods[] = {
//{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z", (void *)android_net_utils_runDhcp },
//...
////;
if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {//Exécuter l'application DHCP pour l'opération d'adresse IP
événement = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED ;
if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler : requête DHCP réussie");
} autre {
événement = EVENT_INTERFACE_CONFIGURATION_FAILED ;
Log.i(TAG, "DhcpHandler : échec de la requête DHCP : " +
NetworkUtils.getDhcpError());
//Si dhcpcd ne parvient pas à allouer l'IP, alors Message.obtain(mTarget, event).sendToTarget();
//WifiNative.disconnectCommand(); c'est-à-dire : static JNINativeMethod gWifiMethods[] = {
//android_net_wifi_disconnectCommand envoie la chaîne "DISCONNECT" [luther.gliethttp]
//Puis exécutez wpa_supplicant_ctrl_iface_process sur le serveur wpa_supplicant
//wpa_supplicant_disassociate
}
synchronisé (ce) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO);
}
Message.obtain(mTarget, event).sendToTarget();
casser;
}
}
}
...
/**
* Envoyer au tracker une notification indiquant une connexion au demandeur
* Le démon a été établi.
*/
//Dans la classe publique ci-dessus WifiMonitor=>ensureSupplicantConnection
//=>
//pendant que (!supplicantConnected) {
// booléen connecté ;
//synchronisé (mWifiStateTracker) {
//connected = WifiNative.connectToSupplicant();//Si la connexion échoue, alors la boucle while essaie jusqu'à ce que la tentative réussisse, ou que oneShot soit défini, une seule tentative
//=>mWifiStateTracker.notifySupplicantConnection();//Si WifiNative.connectToSupplicant() réussit, il sera exécuté
//Appel de mWifiStateTracker.notifySupplicantConnection();.
void notifySupplicantConnection() {//Envoyer un message à l'objet
Message.obtain(this, EVENT_SUPPLICANT_CONNECTION).sendToTarget();
}
void notifyStateChange (SupplicantState newState) {
Message.obtain(this, EVENT_SUPPLICANT_STATE_CHANGED, newState).sendToTarget();
}
...
}
jboolean statique android_net_wifi_setPowerModeCommand (JNIEnv* env, jobject clazz, mode jint)
{
char cmdstr[256];
sprintf(cmdstr, "DRIVER POWERMODE %d", mode);
return doBooleanCommand(cmdstr, "OK");
}
android_net_wifi_setPowerModeCommand
=>doBooleanCommand
=>faireCommande
=>commande_wifi
=>wifi_send_commande
=>wpa_ctrl_request
=> envoyer à wpa_supplicant
Ensuite, wpa_supplicant effectuera les opérations de réception suivantes :
système/extra/wpa_supplicant/main.c
=>wpa_supplicant_add_iface
=>wpa_supplicant_init_iface2
=>wpa_supplicant_ctrl_iface_init
=> Enregistrez les fonctions de traitement du port de contrôle ctrl_conn et du port d'écoute Monitor_conn
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);//fonction de traitement du gestionnaire du port ctrl_conn
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);//Fonction de traitement de rappel du port moniteur_conn, traite les données netlink vers tous les ports d'écoute moniteur_conn
=>wpa_supplicant_ctrl_iface_receive//Pour la méthode de communication Unix
=>wpa_supplicant_ctrl_iface_process
=>Si wpa_cli envoie une commande sous la forme du pilote wpa_cli xxx, alors appelez cette fonction
if (os_strncmp(buf, "DRIVER ", 7) == 0) {//Ignorer les 7 premiers et passer la commande directement
réponse_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, réponse, réponse_size);
=>wpa_supplicant_driver_cmd
=>wpa_drv_driver_cmd
=> Personnalisez la fonction de traitement de l'extension DRIVER, ainsi pour la commande de gestion de l'alimentation passée par java, wpa_drv_driver_cmd recevra la chaîne "POWERMODE 0" ou "POWERMODE 1" [luther.gliethttp]
=================================================== =================================================== =========
jni
=>exécuterDhcp
=>android_net_utils_runDhcp
libs/netutils/dhcp_utils.c
=>dhcp_do_request
=>
char const statique DAEMON_NAME[] = "dhcpcd";
char const statique DAEMON_PROP_NAME[] = "init.svc.dhcpcd";
char const statique DHCP_PROP_NAME_PREFIX[] = "dhcp";
const char *ctrl_prop = "ctl.start";
const char *desired_status = "en cours d'exécution";
snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
DHCP_PROP_NAME_PREFIX,
interface);
property_set(result_prop_name, "");//Set dhcp.eth0.result=""; Attendez que DHCP soit terminé avec succès,
property_set(ctrl_prop, DAEMON_NAME);//Envoyer le mot de commande de démarrage "ctrl.start" au service nommé dhcpcd, qui se trouve dans init.rc
// mot de commande du processus de service dhcpcd dans init.rc
//service dhcpcd /system/bin/dhcpcd eth0
// désactivé
// un coup
wait_for_property(DAEMON_PROP_NAME, wanted_status, 10) ;
//init.c=>processus d'initialisation
//=>handle_property_set_fd est le mot de commande "ctrl.start", donc handle_control_message est appelé pour traiter le message de contrôle.
//=>handle_control_message
//=>msg_start
//=>
// struct service *svc = service_find_by_name(nom);
//service_start(svc);//Démarrer svc, c'est-à-dire exécuter : /system/bin/dhcpcd eth0
//=>service_start
//=>pid = fork();
// if(pid == 0)execve(svc->args[0], (char**) svc->args, (char**) ENV); le processus enfant exécute execve et exécute /system/bin/dhcpcd) , paramètres pour eth0
//=>Sinon, le processus parent, c'est-à-dire le processus init,
//=>notify_service_state(svc->name, "running"); Définir l'accessoire d'état du svc
// snprintf(pname, sizeof(pname), "init.svc.%s", nom);
// property_set(pname, state);//Donc, de cette façon, wait_for_property(DAEMON_PROP_NAME, wanted_status, 10); peut passer [luther.gliethttp] normalement.
wait_for_property(result_prop_name, NULL, 15);//En attente de dhcp.eth0.result=non-null
=================================================== =================================================== =========
system/extra/dhcpcd-4.0.0-beta9/dhcpcd.c
dhcpcd
=>principal
#define SYSCONFDIR "/system/etc/dhcpcd"
#define PACKAGE "dhcpcd"
# définir CONFIG SYSCONFDIR "/" PACKAGE ".conf"
# définir LIBEXECDIR "/system/etc/dhcpcd"
# définir SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks"
=>strlcpy(options->script, SCRIPT, sizeof(options->script));//Options par défaut->script="/system/etc/dhcpcd/dhcpcd-run-hooks"
=>f = fopen(cf ? cf : CONFIG, "r");//Si aucun fichier .conf n'est spécifié, utilisez le fichier .conf par défaut
=>parse_config_line//Analyser le fichier de configuration par défaut "/system/etc/dhcpcd/dhcpcd.conf"
=>parse_option
=> S'il y a une section "script" dans "/system/etc/dhcpcd/dhcpcd.conf"
=>Ensuite, exécutez strlcpy(options->script, oarg, sizeof(options->script));
/*
{"script", argument_requis, NULL, 'c'},
{"option", argument_requis, NULL, 'o'},
Une partie du contenu de "/system/etc/dhcpcd/dhcpcd.conf" est la suivante :
...
option domain_name_servers, domain_name, domain_search, host_name
...
*/
=>dhcp_run
=>handle_dhcp_packet
=>handle_dhcp
=>bind_dhcp
raison = "TIMEOUT"; raison = "LIÉ"; raison = "REBIND"; raison = "RENOUVELER";
system/extra/dhcpcd-4.0.0-beta9/configure.c
=> configure(iface, Reason, state->new, state->old, &state->lease, options, 1);
// Si DHCP expire ou si DHCP réussit, exec_script sera appelé pour exécuter le script.
//Exécuter setprop dhcp.${interface}.result "échec" ou
//Exécuter setprop dhcp.${interface}.result "ok"
=>exec_script(options, iface->nom, raison, NULL, ancien);
=>Ensuite, configure_env transmet la raison dans le script via la variable d'environnement
int exec_script (const struct options *options, const char *iface, const char *raison,
const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
=>pid = fourchette();
=>if(pid == 0)execve(options->script, argv, env);//Le processus enfant exécute le script, la valeur par défaut est "/system/etc/dhcpcd/dhcpcd-run-hooks"
// Le script dhcpcd-run-hooks décidera s'il faut exécuter les fichiers correspondants dans le répertoire system/etc/dhcpcd/dhcpcd-hook/* en fonction de la valeur du niveau
//Notre système contient les 3 fichiers suivants dans le répertoire system/etc/dhcpcd/dhcpcd-hook/*
//configuré en 95
//20-dns.conf
//01-test
=>Le processus parent renvoie while (waitpid(pid, &status, 0) == -1) et attend la fin de l'exécution du script du processus enfant
system/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/20-dns.conf
system/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/95-configuré
...
setprop dhcp.${interface}.ipaddress "${new_ip_address}"
setprop dhcp.${interface}.result "ok"//Définissez l'attribut sur ok
setprop dhcp.${interface}.result "échec"
...
=================================================== =================================================== =========
inet_init, tcp_prot
chaussette->ops->sendmsg(iocb, chaussette, msg, taille);
=>inetsw_array[]
=>inet_stream_ops
=>tcp_sendmsg
=================================================== =================================================== =========
wpa_cli.c
=>principal
=>wpa_cli_interactive
=>wpa_cli_recv_ending(monitor_conn, 0, 0);//Blocage et attente que wpa_supplicant envoie des données
=>Si action_monitor est vrai, quelques traitements simples seront effectués, sinon les données envoyées par wpa_supplicant seront imprimées directement sur la console [luther.gliethttp].