Android SoftAp支持 (一)

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

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

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

Softap打开流程。

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

Android SoftAp支持 (一)_第1张图片Android SoftAp支持 (一)_第2张图片

Android SoftAp支持 (一)_第3张图片

        当你做完这些设置,系统接受的AP设置界面变化打开的响应,从此开启了整个Android SoftAP的序幕。得意

首先./packages/apps/Settings/src/com/android/settings/TetherSettings.java 的onPreferenceChange 函数接收到Softap状态改变信息

[java]  view plain copy print ?
  1. public boolean onPreferenceChange(Preference preference, Object value) {  
  2.     boolean enable = (Boolean) value;  
  3.   
  4.     if (enable) {  
  5.         startProvisioningIfNecessary(WIFI_TETHERING);  
  6.     } else {  
  7.         mWifiApEnabler.setSoftapEnabled(false);  
  8.     }  
  9.     return false;  
  10. }  

 Softap开启时,enable 为真,因而执行startProvisioningIfNecessary(WIFI_TETHERING);

[java]  view plain copy print ?
  1. private void startProvisioningIfNecessary(int choice) {  
  2.     mTetherChoice = choice;  
  3.     if (isProvisioningNeeded()) {  
  4.         Intent intent = new Intent(Intent.ACTION_MAIN);  
  5.         intent.setClassName(mProvisionApp[0], mProvisionApp[1]);  
  6.         startActivityForResult(intent, PROVISION_REQUEST);  
  7.     } else {  
  8.         startTethering();  
  9.     }  
  10. }  

isProvisioningNeeded 用来检测是否需要进行一些准备工作

如果无需准备工作则执行startTethering  大戏即将上演了 期待ing 大笑

[java]  view plain copy print ?
  1. private void startTethering() {  
  2.     switch (mTetherChoice) {  
  3.         case WIFI_TETHERING:  
  4.             mWifiApEnabler.setSoftapEnabled(true);  
  5.             break;  
  6.         case BLUETOOTH_TETHERING:  
  7.             // turn on Bluetooth first  
  8.             break;  
  9.         case USB_TETHERING:  
  10.             setUsbTethering(true);  
  11.             break;  
  12.         default:  
  13.             //should not happen  
  14.             break;  
  15.     }  
  16. }  

这里 mTetherChoice == WIFI_TETHERING 所以继而执行WiFiApEnable.java中的setSoftapEnabled(true)函数

也从此处也跳出了Setting的代码 跳入了Android WIFI 子系统的framework层

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

[java]  view plain copy print ?
  1. public void setSoftapEnabled(boolean enable) {  
  2.     final ContentResolver cr = mContext.getContentResolver();  
  3.     /** 
  4.      * Disable Wifi if enabling tethering 
  5.      */  
  6.     int wifiState = mWifiManager.getWifiState(); //获取当前wifi的状态 如果开启则关闭且保存状态信息到变量中  
  7.     if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) ||  
  8.                 (wifiState == WifiManager.WIFI_STATE_ENABLED))) {  
  9.         mWifiManager.setWifiEnabled(false);  
  10.         Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 1);  
  11.     }  
  12.   
  13.     if (mWifiManager.setWifiApEnabled(null, enable)) {  
  14.         /* Disable here, enabled on receiving success broadcast */  
  15.         mCheckBox.setEnabled(false);  
  16.     } else {  
  17.         mCheckBox.setSummary(R.string.wifi_error);  
  18.     }  
  19.   
  20.     /** 
  21.      *  If needed, restore Wifi on tether disable 
  22.      */  
  23.     if (!enable) {  
  24.         int wifiSavedState = 0;  
  25.         try {  
  26.             wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE);  
  27.         } catch (Settings.SettingNotFoundException e) {  
  28.             ;  
  29.         }  
  30.         if (wifiSavedState == 1) {  
  31.             mWifiManager.setWifiEnabled(true);  
  32.             Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);  
  33.         }  
  34.     }  
  35. }  

上面的代码中我们看到了Google人的考虑事情的周全。首先检测Wifi当前状态如果正在打开或者已经打开则关闭WIFI并将此状态记录下来,以便关闭softap时它能自动恢复到之前打开wifi的状态。 Android代码不愧牛X,这些都能想到...  崇拜那些大牛。

这里调用mWifiManager.setWifiApEnabled(null, enable)    "frameworks/base/wifi/java/android/net/wifi/WifiManager.java"

[java]  view plain copy print ?
  1. public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {  
  2.     try {  
  3.         mService.setWifiApEnabled(wifiConfig, enabled);  
  4.         return true;  
  5.     } catch (RemoteException e) {  
  6.         return false;  
  7.     }  
  8. }  

转向服务层的 setWifiApEnabled  "frameworks/base/services/java/com/android/server/WifiService.java"

[java]  view plain copy print ?
  1. public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {  
  2.     enforceChangePermission();  
  3.     mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);  
  4. }  

从而调用到最基础的也是最重要的Wifi状态机中的   setWifiApEnabled 实例 其实我真搞不懂为什么Android代码要嵌套这么多层去调用,为了安全、方便... 哪个牛人解释一下。

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

[java]  view plain copy print ?
  1. public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {  
  2.     mLastApEnableUid.set(Binder.getCallingUid());  
  3.     if (enable) {  
  4.         /* Argument is the state that is entered prior to load */  
  5.         sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));  
  6.         sendMessage(obtainMessage(CMD_START_AP, wifiConfig));  
  7.     } else {  
  8.         sendMessage(CMD_STOP_AP);  
  9.         /* Argument is the state that is entered upon success */  
  10.         sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));  
  11.     }  
  12. }  

        发送CMD_LOAD_DRIVER状态迁移到mDriverLoadingState 加载AP对应的驱动 这里把WIFI的驱动跟 AP的驱动做了区分,可见SoftAP不仅仅是软件实现的,需要硬件驱动的相应支持。

[java]  view plain copy print ?
  1. class DriverLoadingState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         new Thread(new Runnable() {  
  5.             public void run() {  
  6.                 mWakeLock.acquire();  
  7.                 //enabling state  
  8.                 switch(message.arg1) {  
  9.                     case WIFI_STATE_ENABLING:  
  10.                         setWifiState(WIFI_STATE_ENABLING);  
  11.                         break;  
  12.                     case WIFI_AP_STATE_ENABLING:  
  13.                         setWifiApState(WIFI_AP_STATE_ENABLING);  
  14.                         break;  
  15.                 }  
  16.   
  17.                 if(mWifiNative.loadDriver()) {  
  18.                     if (DBG) log("Driver load successful");  
  19.                     sendMessage(CMD_LOAD_DRIVER_SUCCESS);  
  20.                 } else {  
  21.                     loge("Failed to load driver!");  
  22.                     switch(message.arg1) {  
  23.                         case WIFI_STATE_ENABLING:  
  24.                             setWifiState(WIFI_STATE_UNKNOWN);  
  25.                             break;  
  26.                         case WIFI_AP_STATE_ENABLING:  
  27.                             setWifiApState(WIFI_AP_STATE_FAILED);  
  28.                             break;  
  29.                     }  
  30.                     sendMessage(CMD_LOAD_DRIVER_FAILURE);  
  31.                 }  
  32.                 mWakeLock.release();  
  33.             }  
  34.         }).start();  
  35.     }  
  36.   
  37.     @Override  
  38.     public boolean processMessage(Message message) {  
  39.         if (DBG) log(getName() + message.toString() + "\n");  
  40.         switch (message.what) {  
  41.             case CMD_LOAD_DRIVER_SUCCESS:  
  42.                 transitionTo(mDriverLoadedState);  
  43.                 break;  
  44.             case CMD_LOAD_DRIVER_FAILURE:  
  45.                 transitionTo(mDriverFailedState);  
  46.                 break;  
  47.             default:  
  48.                 return NOT_HANDLED;  
  49.         }  
  50.         return HANDLED;  
  51.     }  
  52. }  

加载驱动成功后 系统迁移到mDriverLoadedState 状态

接收到 CMD_START_AP消息  状态又被迁移至mSoftApStartingState

[java]  view plain copy print ?
  1. class DriverLoadedState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         if (DBG) log(getName() + "\n");  
  5.         EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());  
  6.     }  
  7.     @Override  
  8.     public boolean processMessage(Message message) {  
  9.         if (DBG) log(getName() + message.toString() + "\n");  
  10.         switch(message.what) {  
  11.                 /*  
  12.                 ****** 
  13.                 */  
  14.             case CMD_START_AP:  
  15.                 transitionTo(mSoftApStartingState);  
  16.                 break;  
  17.             default:  
  18.                 return NOT_HANDLED;  
  19.         }  
  20.         return HANDLED;  
  21.     }  
  22. }  

 SoftApStartingState 会检测上层传下的参数的有效性并调用startSoftApWithConfig 配置、打开SoftAP

[java]  view plain copy print ?
  1. class SoftApStartingState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         if (DBG) log(getName() + "\n");  
  5.         EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());  
  6.   
  7.         final Message message = getCurrentMessage();  
  8.         if (message.what == CMD_START_AP) {  
  9.             final WifiConfiguration config = (WifiConfiguration) message.obj;  
  10.   
  11.             if (config == null) {  
  12.                 mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);  
  13.             } else {  
  14.                 mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);  
  15.                 startSoftApWithConfig(config);  
  16.             }  
  17.         } else {  
  18.             throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);  
  19.         }  
  20.     }  
  21.     @Override  
  22.     public boolean processMessage(Message message) {  
  23.         if (DBG) log(getName() + message.toString() + "\n");  
  24.         switch(message.what) {  
  25.             case CMD_LOAD_DRIVER:  
  26.             case CMD_UNLOAD_DRIVER:  
  27.             //....  
  28.             case CMD_STOP_SUPPLICANT:  
  29.             case CMD_START_AP:  
  30.             //....  
  31.         }  
  32.     }  
  33. }  

获取SoftAp的网络配置AP名称 加密方式密码....

进行系统驱动(硬件)的配置。

[java]  view plain copy print ?
  1. private void startSoftApWithConfig(final WifiConfiguration config) {  
  2.     // start hostapd on a seperate thread  
  3.     new Thread(new Runnable() {  
  4.         public void run() {  
  5.             try {  
  6.                 mNwService.startAccessPoint(config, mInterfaceName);  
  7.             } catch (Exception e) {  
  8.                 loge("Exception in softap start " + e);  
  9.                 try {  
  10.                     mNwService.stopAccessPoint(mInterfaceName);  
  11.                     mNwService.startAccessPoint(config, mInterfaceName);  
  12.                 } catch (Exception e1) {  
  13.                     loge("Exception in softap re-start " + e1);  
  14.                     sendMessage(CMD_START_AP_FAILURE);  
  15.                     return;  
  16.                 }  
  17.             }  
  18.             if (DBG) log("Soft AP start successful");  
  19.             sendMessage(CMD_START_AP_SUCCESS);  
  20.         }  
  21.     }).start();  
  22. }  
  23.  //...  

这里调用到了"frameworks/base/services/java/com/android/server/NetworkManagementService.java" 中的startAccessPoint函数

函数如下:

[java]  view plain copy print ?
  1. public void startAccessPoint(  
  2.         WifiConfiguration wifiConfig, String wlanIface) {  
  3.     mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);  
  4.     try {  
  5.         wifiFirmwareReload(wlanIface, "AP");  
  6.         if (wifiConfig == null) {  
  7.             mConnector.execute("softap""set", wlanIface);  
  8.         } else {  
  9.             mConnector.execute("softap""set", wlanIface, wifiConfig.SSID,  
  10.                     getSecurityType(wifiConfig), wifiConfig.preSharedKey);  
  11.         }  
  12.         mConnector.execute("softap""startap");  
  13.     } catch (NativeDaemonConnectorException e) {  
  14.         throw e.rethrowAsParcelableException();  
  15.     }  
  16. }  

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

[java]  view plain copy print ?
  1. int CommandListener::SoftapCmd::runCommand(SocketClient *cli,  
  2.                                         int argc, char **argv) {  
  3.     int rc = 0, flag = 0;  
  4.     char *retbuf = NULL;  
  5.   
  6.     if (argc < 2) {  
  7.         cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Missing argument"false);  
  8.         return 0;  
  9.     }  
  10.   
  11.     if (!strcmp(argv[1], "startap")) {  
  12.         rc = sSoftapCtrl->startSoftap();  
  13.     } else if (!strcmp(argv[1], "stopap")) {  
  14.         rc = sSoftapCtrl->stopSoftap();  
  15.     } else if (!strcmp(argv[1], "fwreload")) {  
  16.         rc = sSoftapCtrl->fwReloadSoftap(argc, argv);  
  17.     } else if (!strcmp(argv[1], "clients")) {  
  18.         rc = sSoftapCtrl->clientsSoftap(&retbuf);  
  19.         if (!rc) {  
  20.             cli->sendMsg(ResponseCode::CommandOkay, retbuf, false);  
  21.             free(retbuf);  
  22.             return 0;  
  23.         }  
  24.     } else if (!strcmp(argv[1], "status")) {  
  25.         asprintf(&retbuf, "Softap service %s",  
  26.                  (sSoftapCtrl->isSoftapStarted() ? "started" : "stopped"));  
  27.         cli->sendMsg(ResponseCode::SoftapStatusResult, retbuf, false);  
  28.         free(retbuf);  
  29.         return 0;  
  30.     } else if (!strcmp(argv[1], "set")) {  
  31.         rc = sSoftapCtrl->setSoftap(argc, argv);  
  32.     } else {  
  33.         cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Unknown cmd"false);  
  34.         return 0;  
  35.     }  
  36.   
  37.     if (!rc) {  
  38.         cli->sendMsg(ResponseCode::CommandOkay, "Softap operation succeeded"false);  
  39.     } else {  
  40.         cli->sendMsg(ResponseCode::OperationFailed, "Softap operation failed"true);  
  41.     }  
  42.   
  43.     return 0;  
  44. }  

首先是"set“ 命令, 调用到c = sSoftapCtrl->setSoftap(argc, argv); 来配置网络

配置即将所有上层的网络设置写到HOSTAPD_CONF_FILE[]    = "/data/misc/wifi/hostapd.conf" 中

("system/netd/SoftapController.cpp")

[java]  view plain copy print ?
  1. /* 
  2.  * Arguments: 
  3.  *  argv[2] - wlan interface 
  4.  *  argv[3] - SSID 
  5.  *  argv[4] - Security 
  6.  *  argv[5] - Key 
  7.  *  argv[6] - Channel 
  8.  *  argv[7] - Preamble 
  9.  *  argv[8] - Max SCB 
  10.  */  
  11. int SoftapController::setSoftap(int argc, char *argv[]) {  
  12.     char psk_str[2*SHA256_DIGEST_LENGTH+1];  
  13.     int ret = 0, i = 0, fd;  
  14.     char *ssid, *iface;  
  15.   
  16.     /* ..... */  
  17.     iface = argv[2];  
  18.   
  19.     char *wbuf = NULL;  
  20.     char *fbuf = NULL;  
  21.   
  22.     if (argc > 3) {  
  23.         ssid = argv[3];  
  24.     } else {  
  25.         ssid = (char *)"AndroidAP";  
  26.     }  
  27.   
  28.     if (argc > 4) {  
  29.         if (!strcmp(argv[4], "wpa-psk")) {  
  30.             generatePsk(ssid, argv[5], psk_str);  
  31.             asprintf(&fbuf, "%swpa=1\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str);  
  32.         } else if (!strcmp(argv[4], "wpa2-psk")) {  
  33.             generatePsk(ssid, argv[5], psk_str);  
  34.             asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str);  
  35.         } else if (!strcmp(argv[4], "open")) {  
  36.             asprintf(&fbuf, "%s", wbuf);  
  37.         }  
  38.     } else {  
  39.         asprintf(&fbuf, "%s", wbuf);  
  40.     }  
  41.   
  42.     fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660);  
  43.     /*............*/  
  44.     if (write(fd, fbuf, strlen(fbuf)) < 0) {  
  45.         ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));  
  46.         ret = -1;  
  47.     }  
  48.     free(wbuf);  
  49.     free(fbuf);  
  50.   
  51.     /* Note: apparently open can fail to set permissions correctly at times */  
  52.     // .......  
  53. }  

然后是"startap"命令调用rc = sSoftapCtrl->startSoftap(); 真正开启Softap 

[java]  view plain copy print ?
  1. int SoftapController::startSoftap() {  
  2.     pid_t pid = 1;  
  3.     int ret = 0;  
  4.   
  5.     if (mPid) {  
  6.         ALOGE("Softap already started");  
  7.         return 0;  
  8.     }  
  9.     if (mSock < 0) {  
  10.         ALOGE("Softap startap - failed to open socket");  
  11.         return -1;  
  12.     }  
  13.   
  14.     if ((pid = fork()) < 0) {  
  15.         ALOGE("fork failed (%s)", strerror(errno));  
  16.         return -1;  
  17.     }  
  18.   
  19.     if (!pid) {  
  20.         ensure_entropy_file_exists();  
  21.         if (execl("/system/bin/hostapd""/system/bin/hostapd",  
  22.                   "-e", WIFI_ENTROPY_FILE,  
  23.                   HOSTAPD_CONF_FILE, (char *) NULL)) {  
  24.             ALOGE("execl failed (%s)", strerror(errno));  
  25.         }  
  26.         ALOGE("Should never get here!");  
  27.         return -1;  
  28.     } else {  
  29.         mPid = pid;  
  30.         ALOGD("Softap startap - Ok");  
  31.         usleep(AP_BSS_START_DELAY);  
  32.     }  
  33.     return ret;  
  34.   
  35. }  

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

转载于:https://my.oschina.net/u/994235/blog/152353

你可能感兴趣的:(Android SoftAp支持 (一))