<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

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

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    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)即調用方法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, 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 (;;) {
    ensureSupplicantConnection();//=>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 request succeeded");
    } 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 JNINativeMethod gWifiMethods[] = {
    //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;
    }
    }
    }
    ...
    /**
    * Send the tracker a notification that a connection to the supplicant
    * daemon has been established.
    */
    //在上面的public class WifiMonitor=>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 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傳遞過來的power電源管理命令,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,
    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));直接拷貝
    /*
    {"script", required_argument, NULL, 'c'},
    {"option", required_argument, NULL, 'o'},
    "/system/etc/dhcpcd/dhcpcd.conf"中的部分內容如下:
    ...
    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);
    //如果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 struct dhcp_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"http://設置屬性為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].

    原地址:http://www.wangchao.net.cn/it/detail_60528.html

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全