android-wifi模組篇之jni監控wifi網路連接、dhcpcd執行和power電源控制

來源:互聯網
上載者:User

淺析android下如何通過jni監控wifi網路連接、dhcpcd執行和power電源控制

=============================================================================================================
libs/android_runtime/android_net_wifi_Wifi.cpp
部分jni介面
static 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
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)即調用方法intAndroidRuntime::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, reply, *reply_len,0);//阻塞等待wpa_supplicant的netlink資料過來
=>如果接收的buf資料區,buf[0]為'<',那麼說明有level層級資訊,所以將'<'...'>'資料剔除,然後wifi_wait_for_event函數返回[luther.gliethttp].
java/android/android/net/wifi/WifiMonitor.java
public class WifiMonitor {
  ...
  public void startMonitoring() {
    new MonitorThread().start();//啟動java線程
  }
  class MonitorThread extends Thread {
    public MonitorThread() {
      super("WifiMonitor");
    }
  public void run() {
    for (;;) {
      ensureSupplicantConnecti

on();//=>WifiNative.connectToSupplicant調用jni函數android_net_wifi_connectToSupplicant
      String eventStr =WifiNative.waitForEvent();//=>調用jni函數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 disconnectedEvent ="DISCONNECTED";
      String eventName =eventStr.substring(eventPrefixLen);//去掉"CTRL-EVENT-"字串
      int nameEnd = eventName.indexOf('');//找到隨後的空格位置,這在wpa_supplicant發送時
//#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED"中,已經內建空格了.
        if (nameEnd != -1)
          eventName = eventName.substring(0, nameEnd);
      int event;
        if(eventName.equals(connectedEvent))//檢測netlink過來的字串action類型
          event = CONNECTED;
        else if (eventName.equals(disconnectedEvent))
          event = DISCONNECTED;
  ...
      int ind = eventStr.indexOf(" - ");//CTRL-EVENT-CONNECTED -Connection to ...
          if (ind != -1)
            eventData = eventStr.substring(ind + 3);
//剔除前置控制字元,將" - "後面的描述字串作為真實資料,繼續處理
  ...
      if (event == STATE_CHANGE) {
          handleSupplicantStateChange(eventData);
        } else if (event == DRIVER_STATE) {
          handleDriverEvent(eventData);
        } else {
          handleEvent(event,eventData);//對於CONNECTED和DISCONNECTED等netlink事件將執行此操作來處理[luther.gliethttp]
          // If supplicant is gone, exit the thread
          if (event == TERMINATING) {
            break;
          }
        }
  ...
      void handleEvent(int event, String remainder) {
      switch (event) {
        case DISCONNECTED:
          handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);
          break;

        case CONNECTED:
          handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);//控制介面顯示
          break;
  ...
}
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) {
    final int eventualWifiState = enable ? WIFI_STATE_ENABLED :WIFI_STATE_DISABLED;
      ...
      if (enable) {
      if (WifiNative.loadDriver()) {
        Log.e(TAG, "Failed to load Wi-Fi driver.");
        updateWifiState(WIFI_STATE_UNKNOWN);
        return false;
      }
      if (WifiNative.startSupplicant()) {
        WifiNative.unloadDriver();
        Log.e(TAG, "Failed to start supplicant daemon.");
        updateWifiState(WIFI_STATE_UNKNOWN);
        return false;
      }
      mWifiStateTracker.startEventLoop();
//啟動MonitorThread線程,等待wpa_supplicant將netlink資料轉寄過來,然後根據netlink動作類型,進一步影響介面顯示[luther.gliethttp].
    }
  ...
}
java/android/android/net/wifi/WifiStateTracker.java
電源管理
private void handleConnectedState() {
  ...
  mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget();//傳遞到下面的handleMessage方法
  ...
}
public void onChange(boolean selfChange) {
  ...
  handleConnectedState();
  ...
}
public class WifiStateTracker extends NetworkStateTracker {
  ...
  public void handleMessage(Message msg) {
    switch (msg.what) {
      case EVENT_SUPPLICANT_CONNECTION:
      case EVENT_NETWORK_STATE_CHANGED:
        handleConnectedState();//調用
  ...
  private class DhcpHandler extends Handler {

    private Handler mTarget;

    public DhcpHandler(Looper looper, Handler target) {
      super(looper);
      mTarget = target;
    }

    public void handleMessage(Message msg) {
      int event;
    //private static final int DRIVER_POWER_MODE_AUTO = 0;
    //private static final int DRIVER_POWER_MODE_ACTIVE = 1;
      switch (msg.what) {
        case EVENT_DHCP_START:
          synchronized (this) {
            WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);//設定電源模式,調用android_net_wifi_setPowerModeCommand
          }
          Log.d(TAG, "DhcpHandler: DHCP request started");
//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申請ip地址操作
            event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
            if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP requestsucceeded");
          } else {
            event = EVENT_INTERFACE_CONFIGURATION_FAILED;
            Log.i(TAG, "DhcpHandler: DHCP request failed: " +
              NetworkUtils.getDhcpError());
//如果dhcpcd分配ip失敗,那麼Message.obtain(mTarget,event).sendToTarget();將執行
//WifiNative.disconnectCommand();即:static JNINativeMethodgWifiMethods[] = {
//android_net_wifi_disconnectCommand發送"DISCONNECT"字串[luther.gliethttp]
//然後在wpa_supplicant服務端執行wpa_supplicant_ctrl_iface_process
//wpa_supplicant_disassociate
          }
          synchronized (this) {
            WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO);
          }
          Message.obtain(mTarget, event).sendToTarget();
          break;
      }
    }
  }
  ...
  
//在上面的public classWifiMonitor=>ensureSupplicantConnection
//=>
//while (!supplicantConnected) {
// boolean connected;
//synchronized (mWifiStateTracker) {
//connected =WifiNative.connectToSupplicant();//如果沒有串連成功,那麼while迴圈嘗試,直到嘗試成功,或者定義了oneShot,僅一次嘗試
//=>mWifiStateTracker.notifySupplicantConnection();//如果WifiNative.connectToSupplicant()成功,那麼將執行
//mWifiStateTracker.notifySupplicantConnection();的調用.
  void notifySupplicantConnection() {//向對象發送message
    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", mode);
  return doBooleanCommand(cmdstr, "OK");
}
android_net_wifi_setPowerModeCommand
=>doBooleanCommand
=>doCommand
=>wifi_command
=>wifi_send_command
=>wpa_ctrl_request
=>send給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連接埠的handler處理函數
 wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);//monitor_conn連接埠的回調處理函數,處理netlink資料到所有monitor_conn監聽連接埠
=>wpa_supplicant_ctrl_iface_receive//對於unix通訊方式
=>wpa_supplicant_ctrl_iface_process
=>如果wpa_cli發送的是wpa_cli driverxxx形式的命令,那麼調用這個函數
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傳遞過來的power電源管理命令,wpa_drv_driver_cmd將收到"POWERMODE0"或者"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,
      interface);
  property_set(result_prop_name,"");//設定dhcp.eth0.result="";等到成功完成dhcp之後,
  property_set(ctrl_prop,DAEMON_NAME);//向名字為dhcpcd的service,發送"ctrl.start"啟動命令字,該service在init.rc中
//init.rc中dhcpcd服務進程命令字
//service dhcpcd /system/bin/dhcpcd eth0
// disabled
// oneshot
  wait_for_property(DAEMON_PROP_NAME, desired_status, 10);
//init.c=>init進程
//=>handle_property_set_fd因為是"ctrl.start"命令字,所以調用handle_control_message處理控制資訊
//=>handle_control_message
//=>msg_start
//=>
// struct service *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的狀態prop
// snprintf(pname, sizeof(pname), "init.svc.%s", name);
// property_set(pname,state);//所以這樣上面wait_for_property(DAEMON_PROP_NAME, desired_status,10);也才能夠正常pass[luther.gliethttp].
  wait_for_property(result_prop_name, NULL,15);//等待dhcp.eth0.result=非空
=============================================================================================================
system/extra/dhcpcd-4.0.0-beta9/dhcpcd.c
dhcpcd
=>main
# define SYSCONFDIR    "/system/etc/dhcpcd"
#define PACKAGE      "dhcpcd"
# define CONFIG      SYSCONFDIR "/" PACKAGE ".conf"
# define LIBEXECDIR    "/system/etc/dhcpcd"
# define SCRIPT      LIBEXECDIR "/" PACKAGE "-run-hooks"
=>strlcpy(options->script, SCRIPT,sizeof(options->script));//預設的options->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"有"script"這個節
=>那麼執行strlcpy(options->script, oarg,sizeof(options->script));直接拷貝

=>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);
//如果dhcp逾時或者dhcp成功,都會調用exec_script來執行指令碼,
//執行setprop dhcp.${interface}.result "failed"或者
//執行setprop dhcp.${interface}.result "ok"
=>exec_script(options, iface->name,reason, NULL, old);
=>然後configure_env通過環境變數將reason傳遞到指令碼中
int exec_script(const struct options *options, const char *iface,const char *reason,
   const struct dhcp_message *dhcpn, const structdhcp_message *dhcpo)
=>pid = fork();
=>if(pid == 0)execve(options->script,argv, env);//子進程執行指令碼,預設"/system/etc/dhcpcd/dhcpcd-run-hooks"
//dhcpcd-run-hooks指令碼會根據level值,決定是否執行system/etc/dhcpcd/dhcpcd-hook/*目錄下的相應檔案
//我們的系統在該system/etc/dhcpcd/dhcpcd-hook/*目錄下有如下3個檔案
//95-configured
//20-dns.conf
//01-test
=>父進程返回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.${interface}.ipaddress "${new_ip_address}"
  setprop dhcp.${interface}.result "ok"//設定屬性為ok
  setprop dhcp.${interface}.result "failed"
  ...
=============================================================================================================
inet_init、tcp_prot
sock->ops->sendmsg(iocb, sock, msg,size);
=>inetsw_array[]
=>inet_stream_ops
=>tcp_sendmsg
=============================================================================================================
wpa_cli.c
=>main
=>wpa_cli_interactive
=>wpa_cli_recv_pending(monitor_conn, 0,0);//阻塞等待wpa_supplicant發送資料過來
=>如果action_monitor為true,那麼將執行一些簡單加工操作,否則將直接將wpa_supplicant發過來的資料列印到console上[luther.gliethttp]

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.