Android で jni を介して Wi-Fi ネットワーク接続、dhcpcd 実行、電源制御を監視する方法の簡単な分析
================================================= ================================================= =========
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)
{
...
return AndroidRuntime::registerNativeMethods(env,
WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));// jni を登録する
}
libs/android_runtime/AndroidRuntime.cpp
静的 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) はメソッド 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);
モニターコン = wpa_ctrl_open(ifname);
wpa_ctrl_attach(モニター_コン);
android_net_wifi_waitForEvent
=>wifi_イベント待ち
=>wpa_ctrl_recv(monitor_conn, buf, &nread);
=>recv(ctrl->s, Reply, *reply_len, 0);// ブロックして、wpa_supplicant のネットリンク データが来るのを待ちます
=>受信した buf データ領域の buf[0] が '<' の場合は、レベル情報があることを意味するため、'<'...'>' データを削除し、wifi_wait_for_event 関数は [luther. gliethttp]。
java/android/android/net/wifi/WifiMonitor.java
パブリック クラス WifiMonitor {
...
public void startMonitoring() {
new MonitorThread().start();//Java スレッドを開始します
}
class MonitorThread extends Thread {
public MonitorThread() {
super("Wifiモニター");
}
public void run() {
のために (;;) {
ensureSupplicantConnection();//=>WifiNative.connectToSupplicant は jni 関数 android_net_wifi_connectToSupplicant を呼び出します
String eventStr = WifiNative.waitForEvent();//=>jni 関数 android_net_wifi_waitForEvent を呼び出す
//プライベート静的最終整数 CONNECTED = 1;
//プライベート静的最終整数 DISCONNECTED = 2;
//プライベート静的最終文字列eventPrefix = "CTRL-EVENT-";
//プライベート静的最終整数eventPrefixLen =eventPrefix.length();
//private static Final String ConnectedEvent = "CONNECTED";
//プライベート静的最終文字列disconnectedEvent = "切断";
StringeventName =evenStr.substring(eventPrefixLen);//「CTRL-EVENT-」文字列を削除します
int nameEnd =eventName.indexOf(' ');//wpa_supplicant が送信される後続のスペース位置を検索します。
//#define WPA_EVENT_CONNECTED 「CTRL-EVENT-CONNECTED」にはスペースが組み込まれています。
if (nameEnd != -1)
イベント名 = イベント名.substring(0, nameEnd);
int イベント;
if (eventName.equals(connectedEvent))//ネットリンクから文字列アクション タイプを検出します
イベント = 接続済み;
else if (eventName.equals(disconnectedEvent))
イベント = 切断されました。
...
int ind =eventStr.indexOf(" - ");//CTRL-EVENT-CONNECTED - ...への接続
if (ind != -1)
イベントデータ = イベントStr.substring(ind + 3);
//先頭の制御文字を削除し、「-」以降の説明文字列を実際のデータとして使用し、処理を続行します
...
if (イベント == STATE_CHANGE) {
handleSupplicantStateChange(eventData);
} else if (event == DRIVER_STATE) {
handleDriverEvent(eventData);
} それ以外 {
handleEvent(event,eventData);//CONNECTED や DISCONNECTED などのネットリンク イベントの場合、この操作は [luther.gliethttp] を処理するために実行されます。
// サプリカントがいなくなった場合はスレッドを終了します
if (イベント == 終了中) {
壊す;
}
}
...
void handleEvent(int イベント, 文字列の残り) {
スイッチ (イベント) {
ケースが切断されました:
handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, 残り);
壊す;
ケースが接続されました:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, 残り);// インターフェイスの表示を制御します
壊す;
...
}
public class WifiStateTracker extends NetworkStateTracker {
...
public void startEventLoop() {
mWifiMonitor.startMonitoring();//上記のMonitorThreadスレッドを開始します
}
...
}
java/services/com/android/server/WifiService.java
public class WifiService extends IWifiManager.Stub {
...
private boolean setWifiEnabledBlocking(boolean enable) {
最終 intevenualWifiState = WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
...
if (有効にする) {
if (WifiNative.loadDriver()) {
Log.e(TAG, "Wi-Fi ドライバーのロードに失敗しました。");
updateWifiState(WIFI_STATE_UNKNOWN);
false を返します。
}
if (WifiNative.startSupplicant()) {
WifiNative.unloadDriver();
Log.e(TAG, "サプリカント デーモンの起動に失敗しました。");
updateWifiState(WIFI_STATE_UNKNOWN);
false を返します。
}
mWifiStateTracker.startEventLoop();
//MonitorThread スレッドを開始し、wpa_supplicant がネットリンク データを転送するのを待ってから、ネットリンク アクション タイプ [luther.gliethttp] に従ってインターフェイスの表示にさらに影響を与えます。
}
...
}
java/android/android/net/wifi/WifiStateTracker.java
電源管理
private void handleConnectedState() {
...
mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget(); // 以下の handleMessage メソッドに渡します。
...
}
public void onChange(boolean selfChange) {
...
ハンドル接続状態();
...
}
public class WifiStateTracker extends NetworkStateTracker {
...
public void handleMessage(メッセージメッセージ) {
スイッチ (msg.what) {
EVENT_SUPPLICANT_CONNECTION の場合:
EVENT_NETWORK_STATE_CHANGED の場合:
handleConnectedState();//呼び出し
...
プライベート クラス DhcpHandler extends Handler {
プライベートハンドラー mTarget;
public DhcpHandler(ルーパールーパー、ハンドラーターゲット) {
スーパー(ルーパー);
mTarget = ターゲット;
}
public void handleMessage(メッセージメッセージ) {
int イベント;
//プライベート静的最終整数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 },
// ...
//};
if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {//IP アドレス操作のための dhcp アプリケーションを実行します
イベント = 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,event).sendToTarget() が実行されます。
//WifiNative.disconnectCommand(); つまり: static JNINativeMethod gWifiMethods[] = {
//android_net_wifi_disconnectCommand は「DISCONNECT」文字列を送信します [luther.gliethttp]
//次に、wpa_supplicant サーバー上で wpa_supplicant_ctrl_iface_process を実行します。
//wpa_supplicant_disassociate
}
同期された (これ) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO);
}
Message.obtain(mTarget, イベント).sendToTarget();
壊す;
}
}
}
...
/**
* サプリカントへの接続の通知をトラッカーに送信します。
* デーモンが確立されました。
*/
//上記の public クラス WifiMonitor=>ensureSupplicantConnection
//=>
//while (!supplicantConnected) {
// ブール値で接続されています。
//同期済み (mWifiStateTracker) {
//connected = WifiNative.connectToSupplicant();//接続が成功しない場合は、試行が成功するか、oneShot が定義されるまで、while ループが試行されます (試行は 1 回のみ)
//=>mWifiStateTracker.notifySupplicantConnection();//WifiNative.connectToSupplicant() が成功すると実行されます
// mWifiStateTracker.notifySupplicantConnection(); の呼び出し。
void NoticeSupplicantConnection() {//オブジェクトにメッセージを送信します
Message.obtain(this, EVENT_SUPPLICANT_CONNECTION).sendToTarget();
}
void NoticeStateChange(SupplicantState newState) {
Message.obtain(this, EVENT_SUPPLICANT_STATE_CHANGED, newState).sendToTarget();
}
...
}
static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env、jobject clazz、jint モード)
{
文字 cmdstr[256];
sprintf(cmdstr, "DRIVER POWERMODE %d", モード);
return doBooleanCommand(cmdstr, "OK");
}
android_net_wifi_setPowerModeコマンド
=>doBooleanコマンド
=>doコマンド
=>wifi_コマンド
=>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 ポートのコールバック処理関数、すべての Monitor_conn リスニング ポートへのネットリンク データを処理します
=>wpa_supplicant_ctrl_iface_receive//UNIX通信方式の場合
=>wpa_supplicant_ctrl_iface_process
=>wpa_cli が wpa_cli driver xxx の形式でコマンドを送信する場合、この関数を呼び出します
if (os_strncmp(buf, "DRIVER ", 7) == 0) {//最初の 7 をスキップしてコマンドを直接渡します
Reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, Reply, Reply_size);
=>wpa_supplicant_driver_cmd
=>wpa_drv_driver_cmd
=> DRIVER 拡張処理関数をカスタマイズして、java によって渡される電源管理コマンドの場合、wpa_drv_driver_cmd が "POWERMODE 0" または "POWERMODE 1" の文字列を受け取るようにします [luther.gliethttp]
================================================= ================================================= =========
ジニ
=>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 = "実行中";
snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
DHCP_PROP_NAME_PREFIX、
インタフェース);
property_set(result_prop_name, "");//dhcp.eth0.result="" を設定します。dhcp が正常に完了するまで待ちます。
property_set(ctrl_prop, DAEMON_NAME);//init.rc にある dhcpcd という名前のサービスに「ctrl.start」起動コマンド ワードを送信します。
//dhcpcd サービスは init.rc 内のコマンドワードを処理します
//サービス dhcpcd /system/bin/dhcpcd eth0
// 無効
// ワンショット
wait_for_property(DAEMON_PROP_NAME、desired_status、10);
//init.c=>初期化プロセス
//=>handle_property_set_fd は「ctrl.start」コマンドワードであるため、制御メッセージを処理するために 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); 子プロセスは execve を実行し、/system/bin/dhcpcd を実行します。 、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,desired_status, 10); は [luther.gliethttp] を正常に渡すことができます。
wait_for_property(result_prop_name, NULL, 15);//dhcp.eth0.result=non-null を待機しています
================================================= ================================================= =========
system/extra/dhcpcd-4.0.0-beta9/dhcpcd.c
dhcpcd
=>メイン
#define SYSCONFDIR "/system/etc/dhcpcd"
#define パッケージ「dhcpcd」
# CONFIG SYSCONFDIR "/" PACKAGE ".conf" を定義します
# LIBEXECDIR "/system/etc/dhcpcd" を定義します
# SCRIPT LIBEXECDIR "/" PACKAGE "-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」デフォルト設定ファイルを解析します
=>解析オプション
⇒「/system/etc/dhcpcd/dhcpcd.conf」に「script」セクションがある場合
=>次に、 strlcpy(options->script, oarg, sizeof(options->script)) を実行します。
/*
{"スクリプト"、必須引数、NULL、'c'}、
{"オプション"、必須引数、NULL、'o'}、
「/system/etc/dhcpcd/dhcpcd.conf」の内容の一部は以下のとおりです。
...
オプション ドメイン名サーバー、ドメイン名、ドメイン検索、ホスト名
...
*/
=>dhcp_run
=>handle_dhcp_packet
=>ハンドル_dhcp
=>バインド_dhcp
理由 = "タイムアウト";理由 = "バウンド";理由 = "リバインド";理由 = "更新";
system/extra/dhcpcd-4.0.0-beta9/configure.c
=>configure(iface,reason,state->new,state->old,&state->lease,options,1);
//dhcp がタイムアウトするか、dhcp が成功すると、exec_script が呼び出されてスクリプトが実行されます。
// setprop を実行 dhcp.${interface}.result "失敗" または
// setprop を実行 dhcp.${interface}.result "ok"
=>exec_script(オプション、iface->名前、理由、NULL、古い);
=>次に、configure_env は環境変数を介して理由をスクリプトに渡します。
int exec_script(const struct options *options, const char *iface, const char *reason,
const struct dhcp_message *dhcpn、const struct dhcp_message *dhcpo)
=>pid = フォーク();
=>if(pid == 0)execve(options->script, argv, env);//子プロセスがスクリプトを実行します、デフォルトは「/system/etc/dhcpcd/dhcpcd-run-hooks」です
//dhcpcd-run-hooks スクリプトは、レベル値に基づいて system/etc/dhcpcd/dhcpcd-hook/* ディレクトリ内の対応するファイルを実行するかどうかを決定します
//私たちのシステムには、system/etc/dhcpcd/dhcpcd-hook/* ディレクトリに次の 3 つのファイルがあります。
//95 構成
//20-dns.conf
//01-テスト
=>親プロセスは while (waitpid(pid, &status, 0) == -1) に戻り、子プロセスのスクリプトの実行が完了するのを待ちます
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.${インターフェース}.ipaddress "${新しい IP アドレス}"
setprop dhcp.${interface}.result "ok"//属性を ok に設定します
setprop dhcp.${interface}.result "失敗しました"
...
================================================= ================================================= =========
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 が true の場合、いくつかの単純な処理操作が実行されます。それ以外の場合、wpa_supplicant によって送信されたデータはコンソール [luther.gliethttp] に直接出力されます。