Android SoftAp支持 (一)

来源:互联网 发布:秦伟平 知乎 编辑:程序博客网 时间:2024/06/05 02:08

SoftAP 打开时调用的相关函数解析

Softap字面意思是用软件实现AP的功能,让你的移动设备可以作为一个路由,让别的站点链接。比如让别人的手机连上你的已经打开AP功能的手机,玩联机游戏或者上网等等

但事实上此功能是需要硬件以及驱动的支持才能真正的实现的。

Softap打开流程。

        在Android系统的Setting界面的wireless配置项中会看到一个“Portable Wi-Fi hotspot” 跟一个"Configure Wi-Fi hotspot setting"选项,可以进入系统配置AP的名称,加密方式,密码等。 如下图

        当你做完这些设置,系统接受的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);            }        }    }

上面的代码中我们看到了Google人的考虑事情的周全。首先检测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 的一些东东 

原创粉丝点击