Android 中SoftAP架構研究

Android中網絡的整體架構如下:




NetworkmanagementService

此模塊運行在SystemService中,負責Java層的實現機制,提供對上層的一些運行接口,當然,上層是通過一些抽象類實現進程間通訊進行訪問的。


NetD

此模塊是C++的Daemon,負責底層部分對於一些關鍵網絡服務的管理。對上面Java服務提供接口,採用進程間通訊的方式。


Wpa_supplicant

此模塊是提供WIFI支持的模塊,不做詳細描述了。在Android中是一個關鍵的底層服務。


Dnsmasq

此服務實現了DHCP Server,用於輔助Hostapd,實現IP的管理。


Hosted

此服務實現了WIFI AP的關鍵服務,直接控制底層設備,此服務正常運行後,其他終端可以搜索到AP,並連接。


以下是啓動WIFI AP 的流程:



以下是代碼說明:


NetworkManagementService

base/wifi/java/android/net/wifi/WifiStateMachine.java

private void startSoftApWithConfig(final WifiConfiguration config)


frameworks/base/services/java/com/android/server/NetworkManagementService.java // 網路管理服務

startAccessPoint {...}

wifiFirmwareReload(wlanIface, "AP");

mConnector.execute("softap", "set", wlanIface);

mConnector.execute("softap", "startap");


system/netd/CommandListener.cpp // 通訊用得CommandListener

CommandListener::SoftapCmd::runCommand



NetD 服務

system/netd/SoftapController.cpp 

SoftapController::startSoftap()

static const char HOSTAPD_CONF_FILE[]    = "/data/misc/wifi/hostapd.conf";

#define WIFI_ENTROPY_FILE "/data/misc/wifi/entropy.bin"
ensure_entropy_file_exists();  // 檢查需要加密文件是否存在
        if (execl("/system/bin/hostapd", "/system/bin/hostapd",  //啓動Hostapd
                  "-e", WIFI_ENTROPY_FILE,

                  HOSTAPD_CONF_FILE, (char *) NULL)) {
            ALOGE("execl failed (%s)", strerror(errno));

}

SoftapController::stopSoftap // 停止Hostapd

kill(mPid, SIGTERM);  // 結束Hostapd
waitpid(mPid, NULL, 0) // 等待結束返回

system/netd/TetherController.cpp  // DHCP daemon 管理

startTethering() {

...

"system/bin/dnsmasq"  // 啓動Dnsmasq後臺進程

...

}

啓動Hostapd

SoftapController::startSoftap()

static const char HOSTAPD_CONF_FILE[]    = "/data/misc/wifi/hostapd.conf";

#define WIFI_ENTROPY_FILE "/data/misc/wifi/entropy.bin"
ensure_entropy_file_exists();  // 檢查需要加密文件是否存在
        if (execl("/system/bin/hostapd", "/system/bin/hostapd",  //啓動Hostapd
                  "-e", WIFI_ENTROPY_FILE,

                  HOSTAPD_CONF_FILE, (char *) NULL)) {
            ALOGE("execl failed (%s)", strerror(errno));

}

SoftapController::stopSoftap // 停止Hostapd

kill(mPid, SIGTERM);  // 結束Hostapd
waitpid(mPid, NULL, 0) // 等待結束返回

WIFI AP 參數設置

int SoftapController::setSoftap(int argc, char *argv[]) {
...
 property_get("wifi.interface", iface, "wlan0");

    char *wbuf = NULL;
    char *fbuf = NULL;

    if (argc > 3) {
        ssid = argv[3];
    } else {
        ssid = (char *)"AndroidAP";
    }

    asprintf(&wbuf, "interface=%s\ndriver=nl80211\nctrl_interface="
            "/data/misc/wifi/hostapd\nssid=%s\nchannel=6\nieee80211n=1\n",
            iface, ssid);

    if (argc > 4) {
        if (!strcmp(argv[4], "wpa-psk")) {
            generatePsk(ssid, argv[5], psk_str);
            asprintf(&fbuf, "%swpa=1\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str);
        } else if (!strcmp(argv[4], "wpa2-psk")) {
            generatePsk(ssid, argv[5], psk_str);
            asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str);
        } else if (!strncmp("wep", argv[4], 3)){
            if((strlen(argv[5]) == 5) || (strlen(argv[5]) == 13))
                asprintf(&fbuf, "%swep_default_key=0\nwep_key0=\"%s\"\n", wbuf,argv[5]);
            else
                asprintf(&fbuf, "%swep_default_key=0\nwep_key0=%s\n", wbuf,argv[5]);
        } else if (!strcmp(argv[4], "open")) {
            asprintf(&fbuf, "%s", wbuf);
        }
    } else {
        asprintf(&fbuf, "%s", wbuf);
    }
    fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660);
    if (fd < 0) {
        ALOGE("Cannot update \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
        free(wbuf);
        free(fbuf);
        return -1;
    }
    if (write(fd, fbuf, strlen(fbuf)) < 0) {
        ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
        ret = -1;
    }
    free(wbuf);
    free(fbuf);
 
    if (fchmod(fd, 0660) < 0) {
        ALOGE("Error changing permissions of %s to 0660: %s",
                HOSTAPD_CONF_FILE, strerror(errno));
        close(fd);
        unlink(HOSTAPD_CONF_FILE);
        return -1;
    }

    if (fchown(fd, AID_SYSTEM, AID_WIFI) < 0) {
        ALOGE("Error changing group ownership of %s to %d: %s",
                HOSTAPD_CONF_FILE, AID_WIFI, strerror(errno));
        close(fd);
        unlink(HOSTAPD_CONF_FILE);
        return -1;
    }

    close(fd);
    return ret;
}

PSK Key 的生成

wpa-psk和wpa2-psk 需要生成PSK

#include <openssl/evp.h>

void SoftapController::generatePsk(char *ssid, char *passphrase, char *psk_str) {
    unsigned char psk[SHA256_DIGEST_LENGTH];
    int j;
    // Use the PKCS#5 PBKDF2 with 4096 iterations
    PKCS5_PBKDF2_HMAC_SHA1(passphrase, strlen(passphrase),
            reinterpret_cast<const unsigned char *>(ssid), strlen(ssid),
            4096, SHA256_DIGEST_LENGTH, psk);
    for (j=0; j < SHA256_DIGEST_LENGTH; j++) {
        sprintf(&psk_str[j<<1], "%02x", psk[j]);
    }
    psk_str[j<<1] = '\0';
}



重載Firmware的機制

hardware/libhardware_legacy/wifi/wifi.c   // wifi interface of HAL

system/netd/SoftapController.cpp wifi_change_fw_path 重載Firmware

hardware/libhardware_legacy/wifi/wifi.c wifi_change_fw_path  重載Firmware

重載固件用得設備節點定義

#define WIFI_DRIVER_FW_PATH_PARAM       "/sys/module/wlan/parameters/fwpath" (原始)   "/sys/module/bcmdhd/parameters/firmware_path" (k900)

固件文件的地址

WIFI_DRIVER_FW_PATH_AP  "/system/etc/firmware/fw_bcmdhd_apsta.bin" 文件地址

Dnsmasq 代碼啓動流程如下

接收內核消息:Iface added wlan0然後就會調用到notifyInterfaceAdded

NetworkManagementService.java

private voidnotifyInterfaceAdded(String iface) {

        for(INetworkManagementEventObserverobs : mObservers) {

            try{

                obs.interfaceAdded(iface);//回調tethering. interfaceAdded

            } catch(Exception ex) {

                Slog.w(TAG, "Observer notifier failed", ex);

            }

        }

    }


 

 

Tethering.java

public void interfaceAdded(String iface) {//被回調到的函數

        if(VDBG) Log.d(TAG, "interfaceAdded "+ iface);

        booleanfound = false;

        booleanusb = false;

        synchronized(mPublicSync) {

            if(isWifi(iface)) {

                found = true;

            }

            if(isUsb(iface)) {

                found = true;

                usb = true;

            }

            if(isBluetooth(iface)) {

                found = true;

            }

            if(found == false) {

                if(VDBG) Log.d(TAG, iface + " is not a tetherable iface, ignoring");

                return;

            }

 

            TetherInterfaceSM sm = mIfaces.get(iface);

            if(sm != null) {

                if(VDBG) Log.d(TAG, "active iface ("+ iface + ") reported as added, ignoring");

                return;

            }

            sm = newTetherInterfaceSM(iface, mLooper, usb);//創建TetherInterfaceSM對象,然後會生成InitialState對象,調用InitialState.enter

            mIfaces.put(iface, sm);

            sm.start();

        }

    }

       classInitialState extendsState {

            @Override

            public voidenter() {

                setAvailable(true);

                setTethered(false);

                sendTetherStateChangedBroadcast();//發送廣播

            }

 

 

private voidsendTetherStateChangedBroadcast() {

        try{

            if(!mConnService.isTetheringSupported()) return;

        } catch(RemoteException e) {

            return;

        }

        ArrayList<String> availableList = newArrayList<String>();

        ArrayList<String> activeList = newArrayList<String>();

        ArrayList<String> erroredList = newArrayList<String>();

        booleanwifiTethered = false;

        booleanusbTethered = false;

        booleanbluetoothTethered = false;

        synchronized(mPublicSync) {

            Setifaces = mIfaces.keySet();

            for(Object iface : ifaces) {

                TetherInterfaceSM sm = mIfaces.get(iface);

                if(sm != null) {

                    if(sm.isErrored()) {

                        erroredList.add((String)iface);

                    } else if(sm.isAvailable()) {

                        availableList.add((String)iface);

                    } else if(sm.isTethered()) {

                        if(isUsb((String)iface)) {

                            usbTethered = true;

                        } else if(isWifi((String)iface)) {

                            wifiTethered = true;

                      } else if(isBluetooth((String)iface)) {

                            bluetoothTethered = true;

                        }

                        activeList.add((String)iface);

                    }

                }

            }

        }

        Intent broadcast = newIntent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);//發送廣播到WifiStateMachine

        broadcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING|

                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);

WifiStateMachine.java

 mContext.registerReceiver(

            newBroadcastReceiver() {

                @Override

                public voidonReceive(Context context, Intent intent) {

                    ArrayList<String> available = intent.getStringArrayListExtra(

                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);

                    ArrayList<String> active = intent.getStringArrayListExtra(

                            ConnectivityManager.EXTRA_ACTIVE_TETHER);

                    sendMessage(CMD_TETHER_STATE_CHANGE, newTetherStateChange(available, active));//發送消息                   CMD_TETHER_STATE_CHANGE

                }

            },newIntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));//接收到廣播
 
classSoftApStartedState extendsState {

        @Override

        public voidenter() {

            if(DBG) log(getName() + "\n");

            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());

        }

        @Override

        public booleanprocessMessage(Message message) {

            if(DBG) log(getName() + message.toString() + "\n");

            switch(message.what) {

                case CMD_STOP_AP:

                    if(DBG) log("Stopping Soft AP");

                    setWifiApState(WIFI_AP_STATE_DISABLING);

 

                    /* We have not tethered at this point, so we just shutdown soft Ap */

                    try{

                        mNwService.stopAccessPoint(mInterfaceName);

                    } catch(Exception e) {

                        loge("Exception in stopAccessPoint()");

                    }

                    transitionTo(mDriverLoadedState);

                    break;

                case CMD_START_AP:

                    // Ignore a start on a running access point

                    break;

                    /* Fail client mode operation when soft AP is enabled */

                case CMD_START_SUPPLICANT:

                   loge("Cannot start supplicant with a running soft AP");

                    setWifiState(WIFI_STATE_UNKNOWN);

                    break;

                case CMD_TETHER_STATE_CHANGE://接收CMD_TETHER_STATE_CHANGE

                    TetherStateChange stateChange = (TetherStateChange) message.obj;

                    if (startTethering(stateChange.available)) {//調用startTethering

                        transitionTo(mTetheringState);

                    }

private boolean startTethering(ArrayList<String> available) {

        boolean wifiAvailable = false;

        checkAndSetConnectivityInstance();

        String[] wifiRegexs = mCm.getTetherableWifiRegexs();

        for (String intf : available) {

            for (String regex : wifiRegexs) {

                if (intf.matches(regex)) {

                    InterfaceConfiguration ifcg = null;

                    try {

                        ifcg = mNwService.getInterfaceConfig(intf);

                        if (ifcg != null) {

                            /* IP/netmask: 192.168.43.1/255.255.255.0 */

                            ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(

                                    "192.168.43.1"), 24);

                            ifcg.interfaceFlags = "[up]";

                            mNwService.setInterfaceConfig(intf, ifcg);

                        }

                    } catch (Exception e) {

                        loge("Error configuring interface " + intf + ", :" + e);

                        return false;

                    }

                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {// 調用ConnectivityManager. tether

                        loge("Error tethering on " + intf);

                        return false;

                    }

ConnectivityManager.java

 public int tether(String iface) {

        try {

            return mService.tether(iface);//調用ConnectivityService.tether

        } catch (RemoteException e) {

            return TETHER_ERROR_SERVICE_UNAVAIL;

        }

    }


ConnectivityService.java

// javadocfrom interface

    public inttether(String iface) {

        enforceTetherChangePermission();

 

        if(isTetheringSupported()) {

            return mTethering.tether(iface);//調用Tethering.tether

        } else{

            returnConnectivityManager.TETHER_ERROR_UNSUPPORTED;

        }

    }

Tethering.java (\\192.168.80.102\tomchen\8680v6\android\frameworks\base\services\java\com\android\server\connectivity)

public int tether(String iface)//調用這個函數

sm.sendMessage(TetherInterfaceSM.CMD_TETHER_REQUESTED);//發送消息

classInitialState extendsState { @Override public voidenter() { setAvailable(true); setTethered(false); sendTetherStateChangedBroadcast(); } @Override public booleanprocessMessage(Message message) { if(DBG) Log.d(TAG, "InitialState.processMessage what="+ message.what); booleanretValue = true; switch(message.what) { case CMD_TETHER_REQUESTED://接收消息 setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR); mTetherMasterSM.sendMessage(TetherMasterSM.CMD_TETHER_MODE_REQUESTED, TetherInterfaceSM.this); transitionTo(mStartingState);//轉到etherModeAliveState break; classTetherModeAliveState extendsTetherMasterUtilState { boolean mTryCell= !WAIT_FOR_NETWORK_TO_SETTLE; @Override public voidenter() { turnOnMasterTetherSettings(); // may transition us out//進入狀態的時候會被調 mTryCell= !WAIT_FOR_NETWORK_TO_SETTLE; // better try something first pass // or crazy tests cases will fail chooseUpstreamType(mTryCell); mTryCell= !mTryCell; }


設置wlan0的IP地址

frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java

mNwService.setInterfaceConfig

system/netd/CommandListener.cpp

else if (!strcmp(argv[1], "setcfg")){
...
ifc_set_addr
...
}

system/core/libnetutils/ifc_utils.c

int ifc_set_addr(const char *name, in_addr_t addr)



Lenovo K900中使能Hostapd的方法

echo "/system/etc/firmware/fw_bcmdhd_apsta.bin"  > /sys/module/bcmdhd/parameters/firmware_path

ifconfig wlan0 up

hostapd -dd hostapd.conf

/system/bin/dnsmasq --keep-in-foreground --no-resolv --no-poll --dhcp-option-force=43,ANDROID_METERED  --pid-file --dhcp-script=/system/bin/dhcp_announce --dhcp-range=2,5,1h

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章