Analisis singkat tentang cara memantau koneksi jaringan wifi, eksekusi dhcpcd, dan kontrol catu daya melalui jni di Android
==================== ==================== =========
libs/android_runtime/android_net_wifi_Wifi.cpp
Bagian dari antarmuka jni
Metode JNINatif statis gMetode Wifi[] = {
{ "loadDriver", "()Z", (batal *)android_net_wifi_loadDriver },
{ "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },//Manajemen daya
{ "connectToSupplicant", "()Z", (batal *)android_net_wifi_connectToSupplicant },
{ "waitForEvent", "()Ljava/lang/String;", (void*) android_net_wifi_waitForEvent },
{ "disconnectCommand", "()Z", (batal *)android_net_wifi_disconnectCommand },
...
};
ke dalam register_android_net_wifi_WifiManager(JNIEnv* env)
{
...
kembalikan AndroidRuntime::registerNativeMethods(env,
WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));//Daftar jni
}
libs/android_runtime/AndroidRuntime.cpp
konstanta statis RegJNIRec gRegJNI[] = {
...
REG_JNI(register_android_net_wifi_WifiManager),
...
};
ke dalam AndroidRuntime::startReg(JNIEnv* env)
{
...
register_jni_procs(gRegJNI, NELEM(gRegJNI), env);
...
}
AndroidRuntime::mulai
=>startReg(env) memanggil metode ke dalam AndroidRuntime::startReg(JNIEnv* env)
==================== ==================== =========
wifi_load_driver
wifi_start_supplicant
=>pastikan_config_file_exists
//Periksa apakah file /data/misc/wifi/wpa_supplicant.conf ada. Jika tidak ada, salin salinan secara dinamis dari /system/etc/wifi/wpa_supplicant.conf.
android_net_wifi_connectToSupplicant
=>wifi_connect_to_supplicant
=>
ctrl_conn = wpa_ctrl_open(jikanama);
monitor_conn = wpa_ctrl_open(jikanama);
wpa_ctrl_attach(monitor_conn);
android_net_wifi_waitForEvent
=>wifi_tunggu_untuk_acara
=>wpa_ctrl_recv(monitor_conn, buf, &nbaca);
=>recv(ctrl->s, reply, *reply_len, 0);//Memblokir dan menunggu data netlink wpa_supplicant datang
=>Jika pada area data buf yang diterima, buf[0] adalah '<', berarti ada informasi level, sehingga data '<'...'>' dihapus, dan kemudian fungsi wifi_wait_for_event mengembalikan [luther. gliethttp].
java/android/android/net/wifi/WifiMonitor.java
WifiMonitor kelas publik {
...
kekosongan publik startMonitoring() {
MonitorThread().start();//Mulai thread java baru
}
kelas MonitorThread memperluas Thread {
Utas Monitor publik() {
super("WifiMonitor");
}
menjalankan kekosongan publik() {
untuk (;;) {
sureSupplicantConnection();//=>WifiNative.connectToSupplicant memanggil fungsi jni android_net_wifi_connectToSupplicant
String eventStr = WifiNative.waitForEvent();//=>Panggil fungsi jni android_net_wifi_waitForEvent
// int akhir statis pribadi TERHUBUNG = 1;
// int final statis pribadi TERHUBUNG = 2;
//String akhir statis pribadi eventPrefix = "CTRL-EVENT-";
//int akhir statis pribadi eventPrefixLen = eventPrefix.length();
//String final statis pribadi terhubungEvent = "TERHUBUNG";
//String final statis pribadi terputusEvent = "TERHUBUNG";
String eventName = eventStr.substring(eventPrefixLen);//Hapus string "CTRL-EVENT-"
int nameEnd = eventName.indexOf(' ');//Cari posisi spasi selanjutnya, yaitu saat wpa_supplicant dikirimkan
//#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED" memiliki spasi bawaan.
jika (namaAkhir != -1)
eventName = eventName.substring(0, namaEnd);
ke dalam acara;
if (eventName.equals(connectedEvent))//Deteksi jenis tindakan string dari netlink
acara = TERHUBUNG;
else if (eventName.equals(disconnectedEvent))
acara = TERHUBUNG;
...
int ind = eventStr.indexOf(" - ");//CTRL-EVENT-CONNECTED - Koneksi ke ...
jika (ind != -1)
eventData = eventStr.substring(ind + 3);
//Hapus karakter kontrol utama, gunakan string deskripsi setelah "-" sebagai data sebenarnya, dan lanjutkan pemrosesan
...
jika (peristiwa == STATE_CHANGE) {
handleSupplicantStateChange(eventData);
} else if (peristiwa == DRIVER_STATE) {
handleDriverEvent(eventData);
} kalau tidak {
handleEvent(event, eventData);//Untuk kejadian netlink seperti CONNECTED dan DISCONNECTED, operasi ini akan dilakukan untuk menangani [luther.gliethttp]
// Jika pemohon sudah tidak ada, keluar dari thread
if (peristiwa == TERMINASI) {
merusak;
}
}
...
void handleEvent(int event, String sisa) {
beralih (acara) {
kasus TERHUBUNG:
handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, sisa);
merusak;
kasus TERHUBUNG:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, sisanya);//Kontrol tampilan antarmuka
merusak;
...
}
WifiStateTracker kelas publik memperluas NetworkStateTracker {
...
kekosongan publik startEventLoop() {
mWifiMonitor.startMonitoring();//Mulai thread MonitorThread di atas
}
...
}
java/services/com/android/server/WifiService.java
WifiService kelas publik memperluas IWifiManager.Stub {
...
boolean pribadi setWifiEnabledBlocking(aktifkan boolean) {
int akhir akhirnyaWifiState = aktifkan ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
...
jika (aktifkan) {
jika (WifiNative.loadDriver()) {
Log.e(TAG, "Gagal memuat driver Wi-Fi.");
perbaruiWifiState(WIFI_STATE_UNKNOWN);
kembali salah;
}
if (WifiNative.startSupplicant()) {
WifiNative.unloadDriver();
Log.e(TAG, "Gagal memulai daemon pemohon.");
perbaruiWifiState(WIFI_STATE_UNKNOWN);
kembali salah;
}
mWifiStateTracker.startEventLoop();
//Mulai thread MonitorThread, tunggu wpa_supplicant meneruskan data netlink, lalu selanjutnya memengaruhi tampilan antarmuka sesuai dengan jenis tindakan netlink [luther.gliethttp].
}
...
}
java/android/android/net/wifi/WifiStateTracker.java
Manajemen daya
kekosongan pribadi handleConnectedState() {
...
mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget(); // Teruskan ke metode handleMessage di bawah
...
}
public void onChange(boolean selfChange) {
...
handleConnectedState();
...
}
WifiStateTracker kelas publik memperluas NetworkStateTracker {
...
public void handleMessage(Pesan pesan) {
beralih (pesan.apa) {
kasus EVENT_SUPPLICANT_CONNECTION:
kasus EVENT_NETWORK_STATE_CHANGED:
handleConnectedState();//panggilan
...
kelas pribadi DhcpHandler memperluas Handler {
mTarget Penangan pribadi;
public DhcpHandler(Looper looper, Target pengendali) {
super(pengulang);
mTarget = sasaran;
}
public void handleMessage(Pesan pesan) {
ke dalam acara;
// int final statis pribadi DRIVER_POWER_MODE_AUTO = 0;
// int final statis pribadi DRIVER_POWER_MODE_ACTIVE = 1;
beralih (pesan.apa) {
kasus EVENT_DHCP_START:
disinkronkan (ini) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);//Setel mode daya dan panggil android_net_wifi_setPowerModeCommand
}
Log.d(TAG, "DhcpHandler: Permintaan DHCP dimulai");
//libs/android_runtime/android_net_NetUtils.cpp
//metode JNINative statis gNetworkUtilMethods[] = {
//{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z", (batal *)android_net_utils_runDhcp },
// ...
//};
if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {//Jalankan aplikasi dhcp untuk operasi alamat ip
acara = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: Permintaan DHCP berhasil");
} kalau tidak {
acara = EVENT_INTERFACE_CONFIGURATION_FAILED;
Log.i(TAG, "DhcpHandler: Permintaan DHCP gagal: " +
NetworkUtils.getDhcpError());
//Jika dhcpcd gagal mengalokasikan IP, maka Message.obtain(mTarget, event).sendToTarget();
//WifiNative.disconnectCommand(); yaitu: statis JNINativeMethod gWifiMethods[] = {
//android_net_wifi_disconnectCommand mengirimkan string "DISCONNECT" [luther.gliethttp]
//Kemudian jalankan wpa_supplicant_ctrl_iface_process di server wpa_supplicant
//wpa_supplicant_disassociate
}
disinkronkan (ini) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO);
}
Pesan.obtain(mTarget, acara).sendToTarget();
merusak;
}
}
}
...
/**
* Kirimkan pelacak pemberitahuan bahwa ada koneksi ke pemohon
* daemon telah dibuat.
*/
//Di kelas publik di atas WifiMonitor=>ensureSupplicantConnection
//=>
//sementara (!pemohonTerhubung) {
// boolean terhubung;
//disinkronkan (mWifiStateTracker) {
//connected = WifiNative.connectToSupplicant();//Jika koneksi tidak berhasil, maka perulangan while akan mencoba hingga upaya tersebut berhasil, atau oneShot ditentukan, hanya satu upaya
//=>mWifiStateTracker.notifySupplicantConnection();//Jika WifiNative.connectToSupplicant() berhasil, maka akan dieksekusi
//Panggilan mWifiStateTracker.notifySupplicantConnection();.
void notifySupplicantConnection() {//Kirim pesan ke objek
Pesan.dapatkan(ini, EVENT_SUPPLICANT_CONNECTION).sendToTarget();
}
void notifyStateChange(SupplicantState newState) {
Pesan.dapatkan(ini, EVENT_SUPPLICANT_STATE_CHANGED, newState).sendToTarget();
}
...
}
jboolean statis android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, mode jint)
{
karakter cmdstr[256];
sprintf(cmdstr, "DRIVER POWERMODE %d", mode);
kembalikan doBooleanCommand(cmdstr, "OK");
}
android_net_wifi_setPowerModeCommand
=>doBooleanCommand
=>lakukanPerintah
=>wifi_command
=>wifi_send_command
=>wpa_ctrl_request
=>kirim ke wpa_supplicant
Kemudian wpa_supplicant akan melakukan operasi penerimaan berikut:
sistem/ekstra/wpa_supplicant/main.c
=>wpa_supplicant_add_iface
=>wpa_supplicant_init_iface2
=>wpa_supplicant_ctrl_iface_init
=>Daftarkan fungsi pemrosesan port kontrol ctrl_conn dan port pendengaran monitor_conn
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);//fungsi pemrosesan handler dari port ctrl_conn
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);//Fungsi pemrosesan panggilan balik dari port monitor_conn, memproses data netlink ke semua port yang mendengarkan monitor_conn
=>wpa_supplicant_ctrl_iface_receive//Untuk metode komunikasi unix
=>wpa_supplicant_ctrl_iface_process
=>Jika wpa_cli mengirimkan perintah berupa driver wpa_cli xxx, maka panggil fungsi ini
if (os_strncmp(buf, "DRIVER ", 7) == 0) {//Lewati 7 yang pertama dan teruskan perintah secara langsung
balasan_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, balasan, ukuran_balas);
=>wpa_supplicant_driver_cmd
=>wpa_drv_driver_cmd
=> Sesuaikan fungsi pemrosesan ekstensi DRIVER, sehingga untuk perintah manajemen daya daya yang diteruskan oleh java, wpa_drv_driver_cmd akan menerima string "POWERMODE 0" atau "POWERMODE 1" [luther.gliethttp]
==================== ==================== =========
jni
=>jalankanDhcp
=>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.mulai";
const char *desired_status = "berjalan";
snprintf(nama_prop_hasil, sizeof(nama_prop_hasil), "%s.%s.hasil",
DHCP_PROP_NAME_PREFIX,
antarmuka);
property_set(result_prop_name, "");//Set dhcp.eth0.result=""; Tunggu hingga dhcp berhasil diselesaikan,
property_set(ctrl_prop, DAEMON_NAME);//Kirim kata perintah startup "ctrl.start" ke layanan bernama dhcpcd, yang ada di init.rc
//kata perintah proses layanan dhcpcd di init.rc
//layanan dhcpcd /system/bin/dhcpcd eth0
// dengan disabilitas
// satu tembakan
wait_for_property(DAEMON_PROP_NAME, status_yang diinginkan, 10);
//init.c=>init proses
//=>handle_property_set_fd adalah kata perintah "ctrl.start", jadi handle_control_message dipanggil untuk memproses pesan kontrol.
//=>handle_control_message
//=>pesan_mulai
//=>
// struct service *svc = service_find_by_name(nama);
//service_start(svc);//Mulai svc, yaitu, jalankan: /system/bin/dhcpcd eth0
//=>layanan_mulai
//=>pid = garpu();
// if(pid == 0)execve(svc->args[0], (char**) svc->args, (char**) ENV); proses anak mengeksekusi execve dan menjalankan /system/bin/dhcpcd , parameter untuk eth0
//=>Jika tidak, proses induk, yaitu proses init, akan melakukannya
//=>notify_service_state(svc->name, "running"); Menyetel prop status dari svc
// snprintf(pname, sizeof(pname), "init.svc.%s", nama);
// property_set(pname, state);//Jadi dengan cara ini, wait_for_property(DAEMON_PROP_NAME, aged_status, 10); dapat melewati [luther.gliethttp] secara normal.
wait_for_property(result_prop_name, NULL, 15);//Menunggu dhcp.eth0.result=non-null
==================== ==================== =========
sistem/ekstra/dhcpcd-4.0.0-beta9/dhcpcd.c
dhcpcd
=>utama
#define SYSCONFDIR "/system/etc/dhcpcd"
#define PAKET "dhcpcd"
# tentukan CONFIG SYSCONFDIR "/" PACKAGE ".conf"
# tentukan LIBEXECDIR "/system/etc/dhcpcd"
# tentukan SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks"
=>strlcpy(options->script, SCRIPT, sizeof(options->script));//Opsi default->script="/system/etc/dhcpcd/dhcpcd-run-hooks"
=>f = fopen(cf ? cf : CONFIG, "r");//Jika tidak ada file .conf yang ditentukan, gunakan file .conf default
=>parse_config_line//Parsing file konfigurasi default "/system/etc/dhcpcd/dhcpcd.conf"
=>parse_option
=>Jika ada bagian "script" di "/system/etc/dhcpcd/dhcpcd.conf"
=>Kemudian jalankan strlcpy(options->script, oarg, sizeof(options->script));
/*
{"skrip", argumen_wajib, NULL, 'c'},
{"opsi", argumen_wajib, NULL, 'o'},
Bagian konten di "/system/etc/dhcpcd/dhcpcd.conf" adalah sebagai berikut:
...
opsi nama_domain_server, nama_domain, pencarian_domain, nama_host
...
*/
=>dhcp_run
=>handle_dhcp_packet
=>handle_dhcp
=>bind_dhcp
alasan = "TIMEOUT";alasan = "TERIKAT";alasan = "REBIND";alasan = "PERBARUI";
sistem/ekstra/dhcpcd-4.0.0-beta9/configure.c
=> konfigurasikan(iface, alasan, status->baru, status->lama, &status->sewa, opsi, 1);
//Jika dhcp time out atau dhcp berhasil, exec_script akan dipanggil untuk mengeksekusi skrip.
//Jalankan setprop dhcp.${interface}.result "gagal" atau
//Jalankan setprop dhcp.${interface}.hasil "ok"
=>exec_script(pilihan, iface->nama, alasan, NULL, lama);
=>Kemudianconfigure_env meneruskan alasannya ke dalam skrip melalui variabel lingkungan
int exec_script(opsi struct const *pilihan, const char *iface, const char *alasan,
const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
=>pid = garpu();
=>if(pid == 0)execve(options->script, argv, env);//Proses anak mengeksekusi skrip, defaultnya adalah "/system/etc/dhcpcd/dhcpcd-run-hooks"
//skrip dhcpcd-run-hooks akan memutuskan apakah akan mengeksekusi file terkait di direktori system/etc/dhcpcd/dhcpcd-hook/* berdasarkan nilai level
//Sistem kami memiliki 3 file berikut di direktori system/etc/dhcpcd/dhcpcd-hook/*
//95-dikonfigurasi
//20-dns.conf
//01-tes
=>Proses induk kembali sementara (waitpid(pid, &status, 0) == -1) dan menunggu eksekusi skrip proses anak selesai
system/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/20-dns.conf
sistem/ekstra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/95-dikonfigurasi
...
setprop dhcp.${interface}.ipaddress "${new_ip_address}"
setprop dhcp.${interface}.result "ok"//Setel atribut ke ok
setprop dhcp.${interface}.hasil "gagal"
...
==================== ==================== =========
inet_init, tcp_prot
kaus kaki->ops->kirim pesan(iocb, kaus kaki, pesan, ukuran);
=>inetsw_array[]
=>inet_stream_ops
=>tcp_sendmsg
==================== ==================== =========
wpa_cli.c
=>utama
=>wpa_cli_interactive
=>wpa_cli_recv_pending(monitor_conn, 0, 0);//Memblokir dan menunggu wpa_supplicant mengirim data
=>Jika action_monitor benar, beberapa operasi pemrosesan sederhana akan dilakukan, jika tidak, data yang dikirim oleh wpa_supplicant akan dicetak langsung ke konsol [luther.gliethttp].