تحليل موجز لكيفية مراقبة اتصال شبكة wifi وتنفيذ dhcpcd والتحكم في مصدر الطاقة من خلال jni ضمن Android
================================================================================================== ================================================================================================== =========
libs/android_runtime/android_net_wifi_Wifi.cpp
جزء من واجهة JNI
JNINativeMethod ثابت gWifiMethods[] = {
{ "loadDriver"، "()Z"، (void *)android_net_wifi_loadDriver }،
{ "setPowerModeCommand"، "(I)Z"، (void*) android_net_wifi_setPowerModeCommand },//إدارة الطاقة
{ "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)
{
...
إرجاع AndroidRuntime::registerNativeMethods(env,
WIFI_PKG_NAME، gWifiMethods، NELEM(gWifiMethods));//تسجيل jni
}
libs/android_runtime/AndroidRuntime.cpp
ثابت ثابت RegJNIRec gRegJNI[] = {
...
REG_JNI(register_android_net_wifi_WifiManager)،
...
};
إنت AndroidRuntime::startReg(JNIEnv* env)
{
...
Register_jni_procs(gRegJNI, NELEM(gRegJNI), env);
...
}
AndroidRuntime::start
=>startReg(env) يستدعي الطريقة int AndroidRuntime::startReg(JNIEnv* env)
================================================================================================== ================================================================================================== =========
wifi_load_driver
wifi_start_supplicant
=>ensure_config_file_exists
//تحقق من وجود الملف /data/misc/wifi/wpa_supplicant.conf، وإذا لم يكن موجودًا، فقم بنسخ نسخة ديناميكيًا من /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, Response, *reply_len, 0);// حظر وانتظار وصول بيانات netlink الخاصة بـ wpa_supplicant
=>إذا كان buf[0] في منطقة بيانات buf المستلمة هو '<'، فهذا يعني أن هناك معلومات المستوى، لذلك تتم إزالة البيانات '<'...'>'، ثم ترجع وظيفة wifi_wait_for_event [luther. gliethttp].
java/android/android/net/wifi/WifiMonitor.java
فئة عامة WifiMonitor {
...
بداية الفراغ العامالمراقبة () {
new MonitorThread().start();//بدء تشغيل موضوع جافا
}
فئة MonitorThread تمتد الموضوع {
مونيتور ثريد العام () {
super("WifiMonitor");
}
تشغيل الفراغ العام () {
ل (؛؛) {
تأكدSupplicantConnection();//=> يستدعي WifiNative.connectToSupplicant وظيفة jni android_net_wifi_connectToSupplicant
String eventsStr = WifiNative.waitForEvent();//=>استدعاء وظيفة jni android_net_wifi_waitForEvent
// نهائي ثابت خاص CONNECTED = 1؛
// نهائي ثابت خاص تم قطع اتصاله = 2؛
// خاص ثابت Final String eventsPrefix = "CTRL-EVENT-";
// خاص ثابت نهائي int eventsPrefixLen = eventsPrefix. length();
// سلسلة نهائية ثابتة خاصة متصلة Event = "CONNECTED"؛
// سلسلة نهائية ثابتة خاصة DisconnectedEvent = "DISCONNECTED";
String eventsName = eventsStr.substring(eventPrefixLen);// قم بإزالة سلسلة "CTRL-EVENT-"
int nameEnd = eventsName.indexOf(' ');// ابحث عن موضع المسافة اللاحق، وهو عند إرسال wpa_supplicant
//#define WPA_EVENT_CONNECTED يحتوي "CTRL-EVENT-CONNECTED" على مسافات مضمنة.
إذا (nameEnd! = -1)
EventName = eventsName.substring(0, nameEnd);
حدث صحيح؛
if (eventName.equals(connectedEvent))// اكتشف نوع إجراء السلسلة من netlink
الحدث = متصل؛
وإلا إذا (eventName.equals(disconnectedEvent))
الحدث = قطع الاتصال؛
...
int ind = eventsStr.indexOf(" - ");//CTRL-EVENT-CONNECTED - الاتصال بـ ...
إذا (إند! = -1)
EventData = eventsStr.substring(ind + 3);
// قم بإزالة أحرف التحكم البادئة، واستخدم سلسلة الوصف بعد "-" كبيانات حقيقية، واستمر في المعالجة
...
إذا (الحدث == STATE_CHANGE) {
HandleSupplicantStateChange(eventData);
} وإلا إذا (الحدث == DRIVER_STATE) {
HandleDriverEvent(eventData);
} آخر {
HandleEvent(event, eventsData);// بالنسبة لأحداث netlink مثل CONNECTED وDISCONNECTED، سيتم تنفيذ هذه العملية للتعامل مع [luther.gliethttp]
// إذا ذهب الداعي، اخرج من الموضوع
إذا (الحدث == إنهاء) {
استراحة؛
}
}
...
مقبض باطل (حدث int، السلسلة المتبقية) {
التبديل (الحدث) {
حالة قطع الاتصال:
HandleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, الباقي);
استراحة؛
حالة متصلة:
HandleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED، الباقي)؛// عرض واجهة التحكم
استراحة؛
...
}
الطبقة العامة WifiStateTracker تمتد NetworkStateTracker {
...
بداية الفراغ العام () {
mWifiMonitor.startMonitoring();// ابدأ موضوع MonitorThread أعلاه
}
...
}
java/services/com/android/server/WifiService.java
الطبقة العامة WifiService تمتد IWifiManager.Stub {
...
مجموعة منطقية خاصةWifiEnabledBlocking(تمكين منطقي) {
Final int في نهاية المطافWifiState = تمكين WIFI_STATE_ENABLED : WIFI_STATE_DISABLED؛
...
إذا (تمكين) {
إذا (WifiNative.loadDriver()) {
Log.e(TAG، "فشل تحميل برنامج تشغيل Wi-Fi.");
updateWifiState(WIFI_STATE_UNKNOWN);
عودة كاذبة.
}
إذا (WifiNative.startSupplicant()) {
WifiNative.unloadDriver();
Log.e(TAG، "فشل في بدء البرنامج الخفي للملتمس.");
updateWifiState(WIFI_STATE_UNKNOWN);
عودة كاذبة.
}
mWifiStateTracker.startEventLoop();
// ابدأ مؤشر ترابط MonitorThread، وانتظر حتى يقوم wpa_supplicant بإعادة توجيه بيانات netlink، ثم يؤثر بشكل أكبر على عرض الواجهة وفقًا لنوع إجراء netlink [luther.gliethttp].
}
...
}
java/android/android/net/wifi/WifiStateTracker.java
إدارة الطاقة
مقبض باطل خاصConnectedState() {
...
mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget(); // قم بالتمرير إلى طريقة HandleMessage أدناه
...
}
الفراغ العام onChange(boolean selfChange) {
...
HandleConnectedState();
...
}
الطبقة العامة WifiStateTracker تمتد NetworkStateTracker {
...
مقبض الفراغ العام (رسالة رسالة) {
التبديل (msg.what) {
الحالة EVENT_SUPPLICANT_CONNECTION:
الحالة EVENT_NETWORK_STATE_CHANGED:
HandleConnectedState();//call
...
فئة خاصة DhcpHandler تمتد المعالج {
معالج خاص mTarget؛
DhcpHandler العامة (Looper Looper، هدف المعالج) {
سوبر (بير)؛
mTarget = target;
}
مقبض الفراغ العام (رسالة رسالة) {
حدث صحيح؛
// نهائي ثابت خاص DRIVER_POWER_MODE_AUTO = 0؛
// نهائي ثابت خاص DRIVER_POWER_MODE_ACTIVE = 1؛
التبديل (msg.what) {
الحالة EVENT_DHCP_START:
متزامن (هذا) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);// اضبط وضع الطاقة واتصل بـ android_net_wifi_setPowerModeCommand
}
Log.d(TAG, "DhcpHandler: بدأ طلب DHCP");
//libs/android_runtime/android_net_NetUtils.cpp
// JNINativeMethod ثابت gNetworkUtilMethods[] = {
//{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z", (void *)android_net_utils_runDhcp },
// ...
//;
إذا (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {// تنفيذ تطبيق dhcp لتشغيل عنوان IP
الحدث = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED؛
if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: نجح طلب DHCP");
} آخر {
الحدث = EVENT_INTERFACE_CONFIGURATION_FAILED؛
Log.i(TAG, "DhcpHandler: فشل طلب DHCP:" +
NetworkUtils.getDhcpError());
// إذا فشل dhcpcd في تخصيص IP، فسيتم تنفيذ message.obtain(mTarget, events).sendToTarget();
//WifiNative.disconnectCommand(); وهو: static JNINativeMethod gWifiMethods[] = {
//android_net_wifi_disconnectCommand يرسل سلسلة "DISCONNECT" [luther.gliethttp]
// ثم قم بتنفيذ wpa_supplicant_ctrl_iface_process على خادم wpa_supplicant
//wpa_supplicant_disassociate
}
متزامن (هذا) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO);
}
message.obtain(mTarget, events).sendToTarget();
استراحة؛
}
}
}
...
/**
* إرسال إشعار للمتتبع بوجود اتصال للمدعو
* تم إنشاء البرنامج الخفي.
*/
// في الفئة العامة أعلاه WifiMonitor=>ensureSupplicantConnection
//=>
//بينما (!supplicantConnected) {
// منطقي متصل؛
// متزامن (mWifiStateTracker) {
//connected = WifiNative.connectToSupplicant();// إذا لم يكن الاتصال ناجحًا، فستحاول الحلقة أثناء المحاولة حتى تنجح المحاولة، أو يتم تحديد oneShot، وهي محاولة واحدة فقط
//=>mWifiStateTracker.notifySupplicantConnection();// إذا نجح WifiNative.connectToSupplicant()، فسيتم تنفيذه
// نداء mWifiStateTracker.notifySupplicantConnection();.
void notifySupplicantConnection() {// أرسل رسالة إلى الكائن
message.obtain(this, EVENT_SUPPLICANT_CONNECTION).sendToTarget();
}
باطل notifyStateChange (SupplicantState newState) {
message.obtain(this, EVENT_SUPPLICANT_STATE_CHANGED, newState).sendToTarget();
}
...
}
jboolean ثابت android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, jint mode)
{
شار cmdstr[256];
sprintf(cmdstr, "DRIVER POWERMODE %d", mode);
return doBooleanCommand(cmdstr, "OK");
}
android_net_wifi_setPowerModeCommand
=>doBooleanCommand
=>فعل الأمر
=>wifi_command
=>wifi_send_command
=>wpa_ctrl_request
=>أرسل إلى wpa_supplicant
ثم سيقوم wpa_supplicant بعمليات الاستلام التالية:
system/extra/wpa_supplicant/main.c
=>wpa_supplicant_add_iface
=>wpa_supplicant_init_iface2
=>wpa_supplicant_ctrl_iface_init
=> قم بتسجيل وظائف المعالجة لمنفذ التحكم ctrl_conn ومنفذ الاستماع Monitor_conn
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);// وظيفة معالجة المعالج لمنفذ ctrl_conn
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);// وظيفة معالجة رد الاتصال لمنفذ Monitor_conn، تعالج بيانات netlink لجميع منافذ الاستماع Monitor_conn
=>wpa_supplicant_ctrl_iface_receive//لطريقة اتصال يونكس
=>wpa_supplicant_ctrl_iface_process
=>إذا أرسل wpa_cli أمرًا على شكل برنامج تشغيل wpa_cli xxx، فقم باستدعاء هذه الوظيفة
if (os_strncmp(buf, "DRIVER", 7) == 0) {// تخطى السبعة الأولى وقم بتمرير الأمر مباشرة
رد_لين = wpa_supplicant_driver_cmd(wpa_s, buf + 7, رد, رد_حجم);
=>wpa_supplicant_driver_cmd
=>wpa_drv_driver_cmd
=> قم بتخصيص وظيفة معالجة ملحق DRIVER، لذلك بالنسبة لأمر إدارة الطاقة الذي تم تمريره بواسطة Java، سيتلقى wpa_drv_driver_cmd السلسلة "POWERMODE 0" أو "POWERMODE 1" [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،
واجهة)؛
property_set(result_prop_name, "");//Set dhcp.eth0.result=""; انتظر حتى يكتمل dhcp بنجاح،
property_set(ctrl_prop, DAEMON_NAME);// أرسل كلمة أمر بدء التشغيل "ctrl.start" إلى الخدمة المسماة dhcpcd، الموجودة في init.rc
// كلمة أمر عملية خدمة dhcpcd في init.rc
//خدمة dhcpcd /system/bin/dhcpcd eth0
// عاجز
// ون شوت
wait_for_property(DAEMON_PROP_NAME, Desit_status, 10);
//init.c=>عملية init
//=>handle_property_set_fd هي كلمة الأمر "ctrl.start"، لذلك يتم استدعاء Handle_control_message لمعالجة رسالة التحكم.
//=>handle_control_message
//=>msg_start
//=>
// خدمة البناء *svc =service_find_by_name(name);
//service_start(svc);// ابدأ تشغيل svc، أي تنفيذ: /system/bin/dhcpcd eth0
//=>service_start
//=>pid = fork();
// if(pid == 0)execve(svc->args[0], (char**) svc->args, (char**) ENV); معلمات eth0
//=>وإلا فإن العملية الأصلية، أي أن عملية init ستفعل
//=>notify_service_state(svc->name, "running"); قم بتعيين خاصية الحالة لـ svc
// snprintf(pname, sizeof(pname), "init.svc.%s", name);
// property_set(pname,state);// بهذه الطريقة، wait_for_property(DAEMON_PROP_NAME, Destination_status, 10); يمكن أن يمرر [luther.gliethttp] بشكل طبيعي.
wait_for_property(result_prop_name, NULL, 15);// في انتظار dhcp.eth0.result=non-null
================================================================================================== ================================================================================================== =========
نظام/إضافي/dhcpcd-4.0.0-beta9/dhcpcd.c
com.dhcpcd
=>الرئيسية
#تعريف SYSCONFDIR "/system/etc/dhcpcd"
#تعريف الحزمة "dhcpcd"
# تعريف تكوين SYSCONFDIR "/" PACKAGE ".conf"
# تعريف LIBEXECDIR "/system/etc/dhcpcd"
# تعريف البرنامج النصي LIBEXECDIR "/" الحزمة "-run-hooks"
=>strlcpy(options->script, SCRIPT, sizeof(options->script));// الخيارات الافتراضية->script="/system/etc/dhcpcd/dhcpcd-run-hooks"
=>f = fopen(cf ? cf : CONFIG, "r");// إذا لم يتم تحديد ملف .conf، استخدم ملف .conf الافتراضي
=>parse_config_line// تحليل ملف التكوين الافتراضي "/system/etc/dhcpcd/dhcpcd.conf"
=>parse_option
=>إذا كان هناك قسم "برنامج نصي" في "/system/etc/dhcpcd/dhcpcd.conf"
=> ثم قم بتنفيذ strlcpy(options->script, oarg, sizeof(options->script));
/*
{"البرنامج النصي"، وسيطة_مطلوبة، NULL، 'c'}،
{"خيار"، وسيطة مطلوبة، NULL، 'o'}،
جزء من المحتوى الموجود في "/system/etc/dhcpcd/dhcpcd.conf" كما يلي:
...
خيار domain_name_servers، domain_name، domain_search، host_name
...
*/
=>dhcp_run
=>handle_dhcp_packet
=>handle_dhcp
=>bind_dhcp
السبب = "المهلة"؛ السبب = "منضم"؛ السبب = "إعادة الربط"؛ السبب = "تجديد"؛
System/extra/dhcpcd-4.0.0-beta9/configure.c
=> تكوين (iface, Reason,state->new,state->old, &state->lease, options, 1);
// إذا انتهت مهلة dhcp أو نجح dhcp، فسيتم استدعاء exec_script لتنفيذ البرنامج النصي.
// تنفيذ setprop dhcp.${interface}.result "failed" أو
// تنفيذ setprop dhcp.${interface}.result "ok"
=>exec_script(options, iface->name, Reason, NULL, old);
=> ثم يقومconfig_env بتمرير السبب إلى البرنامج النصي من خلال متغير البيئة
int exec_script(const struct options *options, const char *iface, const char *reason,
البنية الأساسية dhcp_message *dhcpn، البنية الأساسية dhcp_message *dhcpo)
=>pid = fork();
=>if(pid == 0)execve(options->script, argv, env);// تقوم العملية الفرعية بتنفيذ البرنامج النصي، الافتراضي هو "/system/etc/dhcpcd/dhcpcd-run-hooks"
// سيقرر البرنامج النصي dhcpcd-run-hooks ما إذا كان سيتم تنفيذ الملفات المقابلة في دليل النظام/etc/dhcpcd/dhcpcd-hook/* بناءً على قيمة المستوى
// يحتوي نظامنا على الملفات الثلاثة التالية في دليل النظام/etc/dhcpcd/dhcpcd-hook/*
//95-تكوين
//20-dns.conf
//01-test
=>ترجع العملية الأصلية بينما (waitpid(pid, &status, 0) == -1) وتنتظر حتى يكتمل تنفيذ البرنامج النصي للعملية الفرعية
نظام/إضافي/dhcpcd-4.0.0-beta9/dhcpcd-hooks/20-dns.conf
النظام/إضافي/dhcpcd-4.0.0-beta9/dhcpcd-hooks/95-configured
...
setprop dhcp.${interface}.ipaddress "${new_ip_address}"
setprop dhcp.${interface}.result "ok"// اضبط السمة على "موافق".
setprop dhcp.${interface}.النتيجة "فشل"
...
================================================================================================== ================================================================================================== =========
inet_init، tcp_prot
sock->ops->sendmsg(iocb, sock, msg, size);
=>inetsw_array[]
=>inet_stream_ops
=>tcp_sendmsg
================================================================================================== ================================================================================================== =========
wpa_cli.c
=>الرئيسية
=>wpa_cli_interactive
=>wpa_cli_recv_pending(monitor_conn, 0, 0);// حظر وانتظار wpa_supplicant لإرسال البيانات
=>إذا كان action_monitor صحيحًا، فسيتم تنفيذ بعض عمليات المعالجة البسيطة، وإلا فستتم طباعة البيانات المرسلة بواسطة wpa_supplicant مباشرة إلى وحدة التحكم [luther.gliethttp].