Android SoftAp支援 (一)

來源:互聯網
上載者:User

Softap字面意思是用軟體實現AP的功能,讓你的行動裝置可以作為一個路由,讓別的站台連結。

事實上市需要硬體以及驅動的支援才能真正的實現這個功能。

第一節 Softap開啟流程。

        在Android系統的Setting介面的wireless配置項中會看到一個“Portable Wi-Fi hotspot” 跟一個"Configure Wi-Fi hotspot setting"選項,可以進入系統配置AP的名稱,加密方式,密碼等。 如

當你做完這些設定,系統接受的響應,從此開啟了整個Android SoftAP的啟動序幕。

首先./packages/apps/Settings/src/com/android/settings/TetherSettings.java 的onPreferenceChange 函數接收到Softap狀態改變資訊

    public boolean onPreferenceChange(Preference preference, Object value) {        boolean enable = (Boolean) value;        if (enable) {            startProvisioningIfNecessary(WIFI_TETHERING);        } else {            mWifiApEnabler.setSoftapEnabled(false);        }        return false;    }

 Softap開啟時,enable 為真,因而執行startProvisioningIfNecessary(WIFI_TETHERING);

    private void startProvisioningIfNecessary(int choice) {        mTetherChoice = choice;        if (isProvisioningNeeded()) {            Intent intent = new Intent(Intent.ACTION_MAIN);            intent.setClassName(mProvisionApp[0], mProvisionApp[1]);            startActivityForResult(intent, PROVISION_REQUEST);        } else {            startTethering();        }    }

isProvisioningNeeded 用來檢測是否需要進行一些準備工作

如果無需準備工作則執行startTethering  大戲即將上演了 期待ing

    private void startTethering() {        switch (mTetherChoice) {            case WIFI_TETHERING:                mWifiApEnabler.setSoftapEnabled(true);                break;            case BLUETOOTH_TETHERING:                // turn on Bluetooth first                break;            case USB_TETHERING:                setUsbTethering(true);                break;            default:                //should not happen                break;        }    }

這裡 mTetherChoice == WIFI_TETHERING 所以繼而執行WiFiApEnable.java中的setSoftapEnabled(true)函數

也從此處也跳出了Setting的代碼 跳入了Android WIFI 子系統的framework層

./packages/apps/Settings/src/com/android/settings/wifi/WifiApEnabler.java

    public void setSoftapEnabled(boolean enable) {        final ContentResolver cr = mContext.getContentResolver();        /**         * Disable Wifi if enabling tethering         */        int wifiState = mWifiManager.getWifiState(); //擷取當前wifi的狀態 如果開啟則關閉且儲存狀態資訊到變數中        if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) ||                    (wifiState == WifiManager.WIFI_STATE_ENABLED))) {            mWifiManager.setWifiEnabled(false);            Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 1);        }        if (mWifiManager.setWifiApEnabled(null, enable)) {            /* Disable here, enabled on receiving success broadcast */            mCheckBox.setEnabled(false);        } else {            mCheckBox.setSummary(R.string.wifi_error);        }        /**         *  If needed, restore Wifi on tether disable         */        if (!enable) {            int wifiSavedState = 0;            try {                wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE);            } catch (Settings.SettingNotFoundException e) {                ;            }            if (wifiSavedState == 1) {                mWifiManager.setWifiEnabled(true);                Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);            }        }    }

首先檢測Wifi目前狀態如果正在開啟或者已經開啟則關閉WIFI並將此狀態記錄下來,以便關閉softap時它能自動回復到之前開啟wifi的狀態。 Android代碼不愧牛X這些都能想到...  崇拜那些大牛。

這裡調用mWifiManager.setWifiApEnabled(null, enable)    "frameworks/base/wifi/java/android/net/wifi/WifiManager.java"

    public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {        try {            mService.setWifiApEnabled(wifiConfig, enabled);            return true;        } catch (RemoteException e) {            return false;        }    }

轉向服務層的 setWifiApEnabled  "frameworks/base/services/java/com/android/server/WifiService.java"

    public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {        enforceChangePermission();        mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);    }

從而調用到最基礎的也是最重要的Wifi狀態機器中的   setWifiApEnabled 執行個體 其實我真搞不懂為什麼Android代碼要嵌套這麼多層去調用,為了安全、方便... 哪個牛人解釋一下。

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

    public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {        mLastApEnableUid.set(Binder.getCallingUid());        if (enable) {            /* Argument is the state that is entered prior to load */            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));            sendMessage(obtainMessage(CMD_START_AP, wifiConfig));        } else {            sendMessage(CMD_STOP_AP);            /* Argument is the state that is entered upon success */            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));        }    }

        發送CMD_LOAD_DRIVER狀態遷移到mDriverLoadingState 載入AP對應的驅動 這裡把WIFI的驅動跟 AP的驅動做了區分,可見SoftAP不僅僅是軟體實現的,需要硬體驅動的相應支援。

    class DriverLoadingState extends State {        @Override        public void enter() {            new Thread(new Runnable() {                public void run() {                    mWakeLock.acquire();                    //enabling state                    switch(message.arg1) {                        case WIFI_STATE_ENABLING:                            setWifiState(WIFI_STATE_ENABLING);                            break;                        case WIFI_AP_STATE_ENABLING:                            setWifiApState(WIFI_AP_STATE_ENABLING);                            break;                    }                    if(mWifiNative.loadDriver()) {                        if (DBG) log("Driver load successful");                        sendMessage(CMD_LOAD_DRIVER_SUCCESS);                    } else {                        loge("Failed to load driver!");                        switch(message.arg1) {                            case WIFI_STATE_ENABLING:                                setWifiState(WIFI_STATE_UNKNOWN);                                break;                            case WIFI_AP_STATE_ENABLING:                                setWifiApState(WIFI_AP_STATE_FAILED);                                break;                        }                        sendMessage(CMD_LOAD_DRIVER_FAILURE);                    }                    mWakeLock.release();                }            }).start();        }        @Override        public boolean processMessage(Message message) {            if (DBG) log(getName() + message.toString() + "\n");            switch (message.what) {                case CMD_LOAD_DRIVER_SUCCESS:                    transitionTo(mDriverLoadedState);                    break;                case CMD_LOAD_DRIVER_FAILURE:                    transitionTo(mDriverFailedState);                    break;                default:                    return NOT_HANDLED;            }            return HANDLED;        }    }

載入驅動成功後 系統遷移到mDriverLoadedState 狀態

接收到 CMD_START_AP訊息  狀態又被遷移至mSoftApStartingState

    class DriverLoadedState extends State {        @Override        public void enter() {            if (DBG) log(getName() + "\n");            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());        }        @Override        public boolean processMessage(Message message) {            if (DBG) log(getName() + message.toString() + "\n");            switch(message.what) {                    /*                     ******                    */                case CMD_START_AP:                    transitionTo(mSoftApStartingState);                    break;                default:                    return NOT_HANDLED;            }            return HANDLED;        }    }

 SoftApStartingState 會檢測上層傳下的參數的有效性並調用startSoftApWithConfig 配置、開啟SoftAP

    class SoftApStartingState extends State {        @Override        public void enter() {            if (DBG) log(getName() + "\n");            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());            final Message message = getCurrentMessage();            if (message.what == CMD_START_AP) {                final WifiConfiguration config = (WifiConfiguration) message.obj;                if (config == null) {                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);                } else {                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);                    startSoftApWithConfig(config);                }            } else {                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);            }        }        @Override        public boolean processMessage(Message message) {            if (DBG) log(getName() + message.toString() + "\n");            switch(message.what) {                case CMD_LOAD_DRIVER:                case CMD_UNLOAD_DRIVER:                //....                case CMD_STOP_SUPPLICANT:                case CMD_START_AP:                //....            }        }    }

擷取SoftAp的網路設定AP名稱 加密方式密碼....

進行系統驅動(硬體)的配置。

    private void startSoftApWithConfig(final WifiConfiguration config) {        // start hostapd on a seperate thread        new Thread(new Runnable() {            public void run() {                try {                    mNwService.startAccessPoint(config, mInterfaceName);                } catch (Exception e) {                    loge("Exception in softap start " + e);                    try {                        mNwService.stopAccessPoint(mInterfaceName);                        mNwService.startAccessPoint(config, mInterfaceName);                    } catch (Exception e1) {                        loge("Exception in softap re-start " + e1);                        sendMessage(CMD_START_AP_FAILURE);                        return;                    }                }                if (DBG) log("Soft AP start successful");                sendMessage(CMD_START_AP_SUCCESS);            }        }).start();    }     //...  }

這裡調用到了"frameworks/base/services/java/com/android/server/NetworkManagementService.java" 中的startAccessPoint函數

函數如下:

    public void startAccessPoint(            WifiConfiguration wifiConfig, String wlanIface) {        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);        try {            wifiFirmwareReload(wlanIface, "AP");            if (wifiConfig == null) {                mConnector.execute("softap", "set", wlanIface);            } else {                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,                        getSecurityType(wifiConfig), wifiConfig.preSharedKey);            }            mConnector.execute("softap", "startap");        } catch (NativeDaemonConnectorException e) {            throw e.rethrowAsParcelableException();        }    }

1、下載AP對應的 firmware

wifiFirmwareReload(wlanIface, "AP");

2、設定ap的ssid 加密方式 以及密碼

mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, getSecurityType(wifiConfig), wifiConfig.preSharedKey);

3、運行softap

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

 這裡通過一個NativeDaemonConnector的執行個體mConnector 調用c++程式 具體的實現我是沒看懂 但是知道最後實際調用的函數, 想深入瞭解可以找一些其他的資料看

實際調用到了 "./system/netd/CommandListener.cpp" 中的CommandListener::SoftapCmd::runCommand

int CommandListener::SoftapCmd::runCommand(SocketClient *cli,                                        int argc, char **argv) {    int rc = 0, flag = 0;    char *retbuf = NULL;    if (argc < 2) {        cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Missing argument", false);        return 0;    }    if (!strcmp(argv[1], "startap")) {        rc = sSoftapCtrl->startSoftap();    } else if (!strcmp(argv[1], "stopap")) {        rc = sSoftapCtrl->stopSoftap();    } else if (!strcmp(argv[1], "fwreload")) {        rc = sSoftapCtrl->fwReloadSoftap(argc, argv);    } else if (!strcmp(argv[1], "clients")) {        rc = sSoftapCtrl->clientsSoftap(&retbuf);        if (!rc) {            cli->sendMsg(ResponseCode::CommandOkay, retbuf, false);            free(retbuf);            return 0;        }    } else if (!strcmp(argv[1], "status")) {        asprintf(&retbuf, "Softap service %s",                 (sSoftapCtrl->isSoftapStarted() ? "started" : "stopped"));        cli->sendMsg(ResponseCode::SoftapStatusResult, retbuf, false);        free(retbuf);        return 0;    } else if (!strcmp(argv[1], "set")) {        rc = sSoftapCtrl->setSoftap(argc, argv);    } else {        cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Unknown cmd", false);        return 0;    }    if (!rc) {        cli->sendMsg(ResponseCode::CommandOkay, "Softap operation succeeded", false);    } else {        cli->sendMsg(ResponseCode::OperationFailed, "Softap operation failed", true);    }    return 0;}

首先是"set“ 命令, 調用到c = sSoftapCtrl->setSoftap(argc, argv); 來配置網路

配置即將所有上層的網路設定寫到HOSTAPD_CONF_FILE[]    = "/data/misc/wifi/hostapd.conf" 中

("system/netd/SoftapController.cpp")

/* * Arguments: *  argv[2] - wlan interface *  argv[3] - SSID *  argv[4] - Security *  argv[5] - Key *  argv[6] - Channel *  argv[7] - Preamble *  argv[8] - Max SCB */int SoftapController::setSoftap(int argc, char *argv[]) {    char psk_str[2*SHA256_DIGEST_LENGTH+1];    int ret = 0, i = 0, fd;    char *ssid, *iface;    /* ..... */    iface = argv[2];    char *wbuf = NULL;    char *fbuf = NULL;    if (argc > 3) {        ssid = argv[3];    } else {        ssid = (char *)"AndroidAP";    }    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 (!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 (write(fd, fbuf, strlen(fbuf)) < 0) {        ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));        ret = -1;    }    free(wbuf);    free(fbuf);    /* Note: apparently open can fail to set permissions correctly at times */    // .......}

然後是"startap"命令調用rc = sSoftapCtrl->startSoftap(); 真正開啟Softap 

int SoftapController::startSoftap() {    pid_t pid = 1;    int ret = 0;    if (mPid) {        ALOGE("Softap already started");        return 0;    }    if (mSock < 0) {        ALOGE("Softap startap - failed to open socket");        return -1;    }    if ((pid = fork()) < 0) {        ALOGE("fork failed (%s)", strerror(errno));        return -1;    }    if (!pid) {        ensure_entropy_file_exists();        if (execl("/system/bin/hostapd", "/system/bin/hostapd",                  "-e", WIFI_ENTROPY_FILE,                  HOSTAPD_CONF_FILE, (char *) NULL)) {            ALOGE("execl failed (%s)", strerror(errno));        }        ALOGE("Should never get here!");        return -1;    } else {        mPid = pid;        ALOGD("Softap startap - Ok");        usleep(AP_BSS_START_DELAY);    }    return ret;}

在startSoftap函數中調用了

execl("/system/bin/hostapd", "/system/bin/hostapd", "-e", WIFI_ENTROPY_FILE, HOSTAPD_CONF_FILE, (char *) NULL)

這裡hostapd就是softap的deamon 程式 類似於wifi的的wpa_supplicant

 

至此所有wifi子系統從介面開啟softap 到如何運行調用到deamon程式開啟Softap的流程就是這樣的

之後會介紹到Setting 介面"Portable Wi-Fi"的開啟 以及 hostapd的一些東東

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.