Eine kurze Analyse zur Überwachung der WLAN-Netzwerkverbindung, der DHCP-Ausführung und der Stromversorgungssteuerung über JNI unter Android
=============================================== =============================================== =========
libs/android_runtime/android_net_wifi_Wifi.cpp
Teil der JNI-Schnittstelle
static JNINativeMethod gWifiMethods[] = {
{ "loadDriver", "()Z", (void *)android_net_wifi_loadDriver },
{ "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },//Energieverwaltung
{ "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));//JNI registrieren
}
libs/android_runtime/AndroidRuntime.cpp
static const RegJNIRec gRegJNI[] = {
...
REG_JNI(register_android_net_wifi_WifiManager),
...
};
int AndroidRuntime::startReg(JNIEnv* env)
{
...
register_jni_procs(gRegJNI, NELEM(gRegJNI), env);
...
}
AndroidRuntime::start
=>startReg(env) ruft die Methode int AndroidRuntime::startReg(JNIEnv* env) auf
=============================================== =============================================== =========
wifi_load_driver
wifi_start_supplicant
=>ensure_config_file_exists
//Überprüfen Sie, ob die Datei /data/misc/wifi/wpa_supplicant.conf vorhanden ist. Wenn sie nicht vorhanden ist, kopieren Sie dynamisch eine Kopie aus /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);//Blockieren und Warten auf das Eintreffen der Netlink-Daten von wpa_supplicant
=>Wenn buf[0] im empfangenen Buf-Datenbereich „<“ ist, bedeutet dies, dass Ebeneninformationen vorhanden sind. Daher werden die Daten „<“...“>“ entfernt und die Funktion wifi_wait_for_event gibt dann [luther. glethttp].
java/android/android/net/wifi/WifiMonitor.java
öffentliche Klasse WifiMonitor {
...
public void startMonitoring() {
new MonitorThread().start();//Java-Thread starten
}
Klasse MonitorThread erweitert Thread {
public MonitorThread() {
super("WifiMonitor");
}
public void run() {
für (;;) {
secureSupplicantConnection();//=>WifiNative.connectToSupplicant ruft die JNI-Funktion android_net_wifi_connectToSupplicant auf
String eventStr = WifiNative.waitForEvent();//=>JNI-Funktion aufrufen 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 connectedEvent = "DISCONNECTED";
String eventName = eventStr.substring(eventPrefixLen);//Entfernen Sie die Zeichenfolge „CTRL-EVENT-“.
int nameEnd = eventName.indexOf(' ');//Suchen Sie die nachfolgende Leerzeichenposition, an der wpa_supplicant gesendet wird
//#define WPA_EVENT_CONNECTED „CTRL-EVENT-CONNECTED“ verfügt über integrierte Leerzeichen.
if (nameEnd != -1)
eventName = eventName.substring(0, nameEnd);
int-Ereignis;
if (eventName.equals(connectedEvent))//Erkennen Sie den String-Aktionstyp von Netlink
Ereignis = VERBUNDEN;
sonst wenn (eventName.equals(disconnectedEvent))
event = DISCONNECTED;
...
int ind = eventStr.indexOf(" - ");//CTRL-EVENT-CONNECTED - Verbindung zu ...
if (ind != -1)
eventData = eventStr.substring(ind + 3);
//Entfernen Sie die führenden Steuerzeichen, verwenden Sie die Beschreibungszeichenfolge nach „-“ als echte Daten und fahren Sie mit der Verarbeitung fort
...
if (event == STATE_CHANGE) {
handleSupplicantStateChange(eventData);
} else if (event == DRIVER_STATE) {
handleDriverEvent(eventData);
} anders {
handleEvent(event, eventData);//Für Netlink-Ereignisse wie CONNECTED und DISCONNECTED wird dieser Vorgang ausgeführt, um [luther.gliethttp] zu verarbeiten
// Wenn Supplicant nicht mehr vorhanden ist, Thread verlassen
if (event == TERMINATING) {
brechen;
}
}
...
void handleEvent(int event, String rest) {
Schalter (Ereignis) {
Fall NICHT VERBUNDEN:
handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, Rest);
brechen;
Fall VERBUNDEN:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, Rest);//Anzeige der Steuerschnittstelle
brechen;
...
}
öffentliche Klasse WifiStateTracker erweitert NetworkStateTracker {
...
public void startEventLoop() {
mWifiMonitor.startMonitoring();//Starten Sie den obigen MonitorThread-Thread
}
...
}
java/services/com/android/server/WifiService.java
Die öffentliche Klasse WifiService erweitert IWifiManager.Stub {
...
privater boolescher Wert setWifiEnabledBlocking(boolean enable) {
final int eventualWifiState = aktivieren? WIFI_STATE_ENABLED: WIFI_STATE_DISABLED;
...
if (aktivieren) {
if (WifiNative.loadDriver()) {
Log.e(TAG, „WLAN-Treiber konnte nicht geladen werden.“);
updateWifiState(WIFI_STATE_UNKNOWN);
return false;
}
if (WifiNative.startSupplicant()) {
WifiNative.unloadDriver();
Log.e(TAG, „Supplicant-Daemon konnte nicht gestartet werden.“);
updateWifiState(WIFI_STATE_UNKNOWN);
return false;
}
mWifiStateTracker.startEventLoop();
// Starten Sie den MonitorThread-Thread, warten Sie, bis wpa_supplicant die Netlink-Daten weiterleitet, und beeinflussen Sie dann die Schnittstellenanzeige entsprechend dem Netlink-Aktionstyp [luther.gliethttp] weiter.
}
...
}
java/android/android/net/wifi/WifiStateTracker.java
Energieverwaltung
private void handleConnectedState() {
...
mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget(); // Übergabe an die handleMessage-Methode unten
...
}
public void onChange(boolean selfChange) {
...
handleConnectedState();
...
}
öffentliche Klasse WifiStateTracker erweitert NetworkStateTracker {
...
public void handleMessage(Message msg) {
switch (msg.what) {
Fall EVENT_SUPPLICANT_CONNECTION:
Fall EVENT_NETWORK_STATE_CHANGED:
handleConnectedState();//call
...
Die private Klasse DhcpHandler erweitert Handler {
privater Handler mTarget;
public DhcpHandler(Looper-Looper, Handler-Ziel) {
super(looper);
mTarget = Ziel;
}
public void handleMessage(Message msg) {
int-Ereignis;
//private static final int DRIVER_POWER_MODE_AUTO = 0;
//private static final int DRIVER_POWER_MODE_ACTIVE = 1;
switch (msg.what) {
Fall EVENT_DHCP_START:
synchronisiert (dies) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);// Stellen Sie den Energiemodus ein und rufen Sie android_net_wifi_setPowerModeCommand auf
}
Log.d(TAG, „DhcpHandler: DHCP-Anfrage gestartet“);
//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)) {// DHCP-Anwendung für IP-Adressbetrieb ausführen
event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
if (LOCAL_LOGD) Log.v(TAG, „DhcpHandler: DHCP-Anfrage erfolgreich“);
} anders {
event = EVENT_INTERFACE_CONFIGURATION_FAILED;
Log.i(TAG, „DhcpHandler: DHCP-Anfrage fehlgeschlagen:“ +
NetworkUtils.getDhcpError());
//Wenn dhcpcd keine IP zuweisen kann, wird Message.obtain(mTarget, event).sendToTarget(); ausgeführt
//WifiNative.disconnectCommand(); das heißt: static JNINativeMethod gWifiMethods[] = {
//android_net_wifi_disconnectCommand sendet die Zeichenfolge „DISCONNECT“ [luther.gliethttp]
//Führen Sie dann wpa_supplicant_ctrl_iface_process auf dem wpa_supplicant-Server aus
//wpa_supplicant_disassociate
}
synchronisiert (dies) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO);
}
Message.obtain(mTarget, event).sendToTarget();
brechen;
}
}
}
...
/**
* Senden Sie dem Tracker eine Benachrichtigung, dass eine Verbindung zum Supplicant hergestellt wurde
* Daemon wurde eingerichtet.
*/
//In der obigen öffentlichen Klasse WifiMonitor=>ensureSupplicantConnection
//=>
//while (!supplicantConnected) {
// boolesche Verbindung;
//synchronisiert (mWifiStateTracker) {
//connected = WifiNative.connectToSupplicant();//Wenn die Verbindung nicht erfolgreich ist, wird die while-Schleife versucht, bis der Versuch erfolgreich ist, oder oneShot definiert ist, nur ein Versuch
//=>mWifiStateTracker.notifySupplicantConnection();//Wenn WifiNative.connectToSupplicant() erfolgreich ist, wird es ausgeführt
//Aufruf von mWifiStateTracker.notifySupplicantConnection();.
void notifySupplicantConnection() {//Nachricht an das Objekt senden
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 cmdstr[256];
sprintf(cmdstr, "DRIVER POWERMODE %d", Modus);
return doBooleanCommand(cmdstr, "OK");
}
android_net_wifi_setPowerModeCommand
=>doBooleanCommand
=>doCommand
=>wifi_command
=>wifi_send_command
=>wpa_ctrl_request
=>an wpa_supplicant senden
Dann führt wpa_supplicant die folgenden Empfangsvorgänge aus:
system/extra/wpa_supplicant/main.c
=>wpa_supplicant_add_iface
=>wpa_supplicant_init_iface2
=>wpa_supplicant_ctrl_iface_init
=>Registrieren Sie die Verarbeitungsfunktionen des Steuerports ctrl_conn und des Überwachungsports monitor_conn
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);// Handler-Verarbeitungsfunktion des ctrl_conn-Ports
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); // Rückrufverarbeitungsfunktion des Monitor_Conn-Ports, verarbeitet Netlink-Daten an alle Monitor_Conn-Abhörports
=>wpa_supplicant_ctrl_iface_receive//Für Unix-Kommunikationsmethode
=>wpa_supplicant_ctrl_iface_process
=>Wenn wpa_cli einen Befehl in Form von wpa_cli-Treiber xxx sendet, rufen Sie diese Funktion auf
if (os_strncmp(buf, "DRIVER ", 7) == 0) {//Überspringen Sie die ersten 7 und übergeben Sie den Befehl direkt
Reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, Reply, Reply_size);
=>wpa_supplicant_driver_cmd
=>wpa_drv_driver_cmd
=> Passen Sie die Verarbeitungsfunktion der DRIVER-Erweiterung an, sodass wpa_drv_driver_cmd für den von Java übergebenen Energieverwaltungsbefehl die Zeichenfolge „POWERMODE 0“ oder „POWERMODE 1“ erhält [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,
Schnittstelle);
property_set(result_prop_name, "");//Set dhcp.eth0.result=""; Warten Sie, bis DHCP erfolgreich abgeschlossen ist.
property_set(ctrl_prop, DAEMON_NAME);//Senden Sie das Startbefehlswort „ctrl.start“ an den Dienst namens dhcpcd, der sich in init.rc befindet
//dhcpcd-Dienstprozessbefehlswort in init.rc
//Dienst dhcpcd /system/bin/dhcpcd eth0
// deaktiviert
// Oneshot
wait_for_property(DAEMON_PROP_NAME, gewünschter_Status, 10);
//init.c=>Init-Prozess
//=>handle_property_set_fd ist das Befehlswort „ctrl.start“, daher wird handle_control_message aufgerufen, um die Steuernachricht zu verarbeiten.
//=>handle_control_message
//=>msg_start
//=>
// struct service *svc = service_find_by_name(name);
//service_start(svc);//SVC starten, also ausführen: /system/bin/dhcpcd eth0
//=>service_start
//=>pid = fork();
// if(pid == 0)execve(svc->args[0], (char**) svc->args, (char**) ENV); der untergeordnete Prozess führt execve aus und führt /system/bin/dhcpcd aus , Parameter für eth0
//=>Andernfalls wird der übergeordnete Prozess, also der Init-Prozess, dies tun
//=>notify_service_state(svc->name, "running"); Setzt die Statusstütze des svc
// snprintf(pname, sizeof(pname), "init.svc.%s", name);
// property_set(pname, state);//Auf diese Weise kann wait_for_property(DAEMON_PROP_NAME, wünschte_status, 10); [luther.gliethttp] normal übergeben.
wait_for_property(result_prop_name, NULL, 15);//Warten auf dhcp.eth0.result=non-null
=============================================== =============================================== =========
system/extra/dhcpcd-4.0.0-beta9/dhcpcd.c
dhcpcd
=>Haupt
#define SYSCONFDIR "/system/etc/dhcpcd"
#define PAKET „dhcpcd“
# definiere CONFIG SYSCONFDIR "/" PACKAGE ".conf"
# definiere LIBEXECDIR „/system/etc/dhcpcd“
# define SCRIPT LIBEXECDIR „/“ PACKAGE „-run-hooks“
=>strlcpy(options->script, SCRIPT, sizeof(options->script));//Standardoptionen->script="/system/etc/dhcpcd/dhcpcd-run-hooks"
=>f = fopen(cf ? cf : CONFIG, "r");//Wenn keine .conf-Datei angegeben ist, verwenden Sie die Standard-.conf-Datei
=>parse_config_line//Parsen Sie die Standardkonfigurationsdatei „/system/etc/dhcpcd/dhcpcd.conf“.
=>parse_option
=>Wenn es einen Abschnitt „script“ in „/system/etc/dhcpcd/dhcpcd.conf“ gibt
=>Dann führen Sie strlcpy(options->script, oarg, sizeof(options->script)) direkt aus
/*
{"script", erforderliches_Argument, NULL, 'c'},
{"Option", erforderliches_Argument, NULL, 'o'},
Ein Teil des Inhalts in „/system/etc/dhcpcd/dhcpcd.conf“ lautet wie folgt:
...
Option domain_name_servers, domain_name, domain_search, host_name
...
*/
=>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);
//Wenn DHCP das Zeitlimit überschreitet oder DHCP erfolgreich ist, wird exec_script aufgerufen, um das Skript auszuführen.
//Setprop dhcp.${interface}.result „fehlgeschlagen“ ausführen oder
//setprop dhcp.${interface}.result „ok“ ausführen
=>exec_script(options, iface->name, reason, NULL, old);
=>Dann übergibt configure_env den Grund über die Umgebungsvariable an das Skript
int exec_script(const struct options *options, const char *iface, const char *reason,
const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
=>pid = fork();
=>if(pid == 0)execve(options->script, argv, env);//Der untergeordnete Prozess führt das Skript aus, Standard ist „/system/etc/dhcpcd/dhcpcd-run-hooks“
Das //dhcpcd-run-hooks-Skript entscheidet anhand des Ebenenwerts, ob die entsprechenden Dateien im Verzeichnis system/etc/dhcpcd/dhcpcd-hook/* ausgeführt werden
//Unser System verfügt über die folgenden 3 Dateien im Verzeichnis system/etc/dhcpcd/dhcpcd-hook/*
//95-konfiguriert
//20-dns.conf
//01-test
=>Der übergeordnete Prozess gibt while (waitpid(pid, &status, 0) == -1) zurück und wartet auf den Abschluss der Skriptausführung des untergeordneten Prozesses
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"//Setzen Sie das Attribut auf ok
setprop dhcp.${interface}.result „fehlgeschlagen“
...
=============================================== =============================================== =========
inet_init, tcp_prot
sock->ops->sendmsg(iocb, sock, msg, size);
=>inetsw_array[]
=>inet_stream_ops
=>tcp_sendmsg
=============================================== =============================================== =========
wpa_cli.c
=>Haupt
=>wpa_cli_interactive
=>wpa_cli_recv_pending(monitor_conn, 0, 0);//Blockiert und wartet darauf, dass wpa_supplicant Daten sendet
=>Wenn action_monitor wahr ist, werden einige einfache Verarbeitungsvorgänge ausgeführt, andernfalls werden die von wpa_supplicant gesendeten Daten direkt an die Konsole [luther.gliethttp] gedruckt.