zoukankan      html  css  js  c++  java
  • Android SoftAp支持 (一)

    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的一些东东

  • 相关阅读:
    用于对象JSON.parse()和JSON.stringify()
    注册地址从零开始学建站域名篇
    类数据成员点类派生出圆类
    事件原因Stack Overflow at line:0 IE下解决方案
    连接参数SqlServer跨服务器查询
    进程子进程linux系统编程之进程(二):fork函数详细分析
    插件在线BookBlock:一个实现翻页效果的jQuery插件
    区域实现Android实现图片的裁剪(不调用系统功能)
    方法数据库android轻量型数据库sqlite的使用方法汇总
    编译用户Orcle的package中访问其它Schema的表
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3106758.html
Copyright © 2011-2022 走看看