Android Wifi work&nbsp…

来源:互联网 发布:淘宝双11营销方式 编辑:程序博客网 时间:2024/05/17 05:03

Android Wifi work station Framework and Architecture

with wpa_supplicant 0.8.X, BCM4329.

转载请注明出处。

Settings/Wifi UI part structure
WifiSettings是主对话框

167

168   @Override

169    publicvoid onActivityCreated(Bundle savedInstanceState) {

170       // We don't call super.onActivityCreated() here, since it assumeswe already set up

171       // Preference (probably in onCreate()), while WifiSettingsexceptionally set it up in

172       // this method.

173

174      mWifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);

175      mWifiManager.asyncConnect(getActivity(), newWifiServiceHandler());

176       if (savedInstanceState != null

177               &&savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)){

178           mDlgEdit =savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);

179           mAccessPointSavedState =savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);

180       }

1101    public void asyncConnect(Context srcContext, Handler srcHandler){

1102       mAsyncChannel.connect(srcContext, srcHandler, getMessenger());

1103    }

establish a half connect between WifiManager and WifiService.

1117    publicvoid connectNetwork(WifiConfiguration config) {

1118       if (config == null) {

1119           return;

1120       }

1121       mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, config);

1122    }

Activity创建的时候会建立和WifiService的AsyncChannel的连接,WifiService同样会建立一个AsyncChannelHandler来处理来自这个Channel的命令消息。AsyncChannel的所谓异步主要用来传递比较耗时的操作并把结果返回给原请求者。AsyncChannel两端分别有一个MessageHandler,srcHandler请求destHandler,destHandler把结果发回给srcHandler,主要是用于处理比较耗时的操作且在WifiManager中处理返回结果。

严格来说,在WifiService的Interface方法之外再做出一个通道提供额外的方法并不是一个什么好的设计。命令是可以通过同步接口发送给WifiService的,但是WifiService的处理结果如果通过broadcast或intent给WifiManager,则又解耦的过度;如果WifiManager实现一个binder做eventsink,又有点小题大做,所以这儿引入这么个AsyncChannel实在是不得以而为之。

WifiSettings界面使能Wifi时会使用定时器请求扫描,获取扫描结果,列出AP列表。Scanner使用定时器,周期性向WifiService请求主动扫描,定时原理是发出本次扫描请求后延迟1s发送下次请求。相关代码如下:

    privateclass Scanner extends Handler {

       private int mRetry = 0;

 

       void resume() {

           if (!hasMessages(0)) {

               sendEmptyMessage(0);

           }

       }

 

       void forceScan() {

           removeMessages(0);

           sendEmptyMessage(0);

       }

 

       void pause() {

           mRetry = 0;

           removeMessages(0);

       }

 

       @Override

       public void handleMessage(Message message) {

           if (mWifiManager.startScanActive()) {

               mRetry = 0;

           } else if (++mRetry >= 3) {

               mRetry = 0;

               Toast.makeText(getActivity(), R.string.wifi_fail_to_scan,

                       Toast.LENGTH_LONG).show();

               return;

           }

           sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);

       }

    }

列出AP列表的代码如下:

In WifiSettings.java

       final List results = mWifiManager.getScanResults(); //同步操作

       if (results != null) {

           for (ScanResult result : results) {

               // Ignore hidden and ad-hoc networks.

              if (result.SSID == null || result.SSID.length() == 0 ||result.capabilities.contains("[IBSS]")) {

                  continue;

              }

               boolean found = false;

               for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {

                   if (accessPoint.update(result))

                       found = true;

               }

               if (!found) {

                   AccessPoint accessPoint = new AccessPoint(getActivity(),result);

                   accessPoints.add(accessPoint);

                   apMap.put(accessPoint.ssid, accessPoint);

               }

           }

       }

 

    private voidupdateAccessPoints() {

       final int wifiState = mWifiManager.getWifiState();

 

       switch (wifiState) {

           case WifiManager.WIFI_STATE_ENABLED:

               // AccessPoints are automatically sorted with TreeSet.

               final Collection accessPoints = constructAccessPoints();

               getPreferenceScreen().removeAll();

               if (mInXlSetupWizard) {

                   ((WifiSettingsForSetupWizardXL)getActivity()).onAccessPointsUpdated(

                           getPreferenceScreen(), accessPoints);

               } else {

                   for (AccessPoint accessPoint : accessPoints) {

                       // When WAPI is not customized to be on all

                       // WAPI APs will be invisible

                      if (accessPoint.isVisible()) {

                           getPreferenceScreen().addPreference(accessPoint);

                       }

                   }

               }

               break;

 

           case WifiManager.WIFI_STATE_ENABLING:

               getPreferenceScreen().removeAll();

               break;

 

           case WifiManager.WIFI_STATE_DISABLING:

               addMessagePreference(R.string.wifi_stopping);

               break;

 

           case WifiManager.WIFI_STATE_DISABLED:

               addMessagePreference(R.string.wifi_empty_list_wifi_off);

               break;

       }

    }

可以看出对ADHOC的AP,隐藏AP(无SSID的)做了过滤处理。

当在AP列表上,长按某个AP时弹出三菜单Menu - ContextMenu。

这个Menu主要是连接该AP,修改该AP,忘记该AP,其处理代码如下:

   @Override

    publicboolean onContextItemSelected(MenuItem item) {

       if (mSelectedAccessPoint == null) {

           return super.onContextItemSelected(item);

       }

       switch (item.getItemId()) {

           case MENU_ID_CONNECT: {

               if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {

                   if (!requireKeyStore(mSelectedAccessPoint.getConfig())) {

                       mWifiManager.connectNetwork(mSelectedAccessPoint.networkId);

                   }

               } else if (mSelectedAccessPoint.security ==AccessPoint.SECURITY_NONE) {

                   

                   mSelectedAccessPoint.generateOpenNetworkConfig();

                   mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());

               } else {

                   showConfigUi(mSelectedAccessPoint, true);

               }

               return true;

           }

           case MENU_ID_FORGET: {

               mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId);

               return true;

           }

           case MENU_ID_MODIFY: {

               showConfigUi(mSelectedAccessPoint, true);

               return true;

           }

       }

       return super.onContextItemSelected(item);

    }

可以看出如果该AP已经经过配置,那么直接连接,如果没有经过配置且该AP没有密码,那么直接连接,否则则弹出配置对话框先进行配置(选择加密类型和输入密码)。

当点击AP列表中的某个AP时,直接根据该AP的情况进行处理,代码如下:

387   @Override

388    publicboolean onPreferenceTreeClick(PreferenceScreen screen, Preferencepreference) {

389       if (preference instanceof AccessPoint) {

390           mSelectedAccessPoint = (AccessPoint) preference;

391           

392           if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE&&

393                   mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {

394               mSelectedAccessPoint.generateOpenNetworkConfig();

395               mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());

396           } else {

397               showConfigUi(mSelectedAccessPoint, false);

398           }

399       } else {

400           return super.onPreferenceTreeClick(screen, preference);

401       }

402       return true;

403    }

可以看出和长按菜单的处理逻辑相似。

当使用Setttings主界面或Settings/Wifi界面的Switcher开关Wifi时,处理代码会调用到mWifiEnabler的setSwitch方法,然后会通知其listener,涉及方法如下:

88    publicvoid setSwitch(Switch switch_) {

89       if (mSwitch == switch_) return;

90       mSwitch.setOnCheckedChangeListener(null);

91       mSwitch = switch_;

92       mSwitch.setOnCheckedChangeListener(this);

93

94       final int wifiState = mWifiManager.getWifiState();

95       boolean isEnabled = wifiState ==WifiManager.WIFI_STATE_ENABLED;

96       boolean isDisabled = wifiState ==WifiManager.WIFI_STATE_DISABLED;

97       mSwitch.setChecked(isEnabled);

98       mSwitch.setEnabled(isEnabled || isDisabled);

99    }

100

101    publicvoid onCheckedChanged(CompoundButton buttonView, boolean isChecked){

102       //Do nothing if called as a result of a state machine event

103       if (mStateMachineEvent) {

104           return;

105       }

106       // Show toast message if Wi-Fi is not allowed in airplane mode

107       if (isChecked && !WirelessSettings.isRadioAllowed(mContext,Settings.System.RADIO_WIFI)) {

108           Toast.makeText(mContext, R.string.wifi_in_airplane_mode,Toast.LENGTH_SHORT).show();

109           // Reset switch to off. No infinite check/listenenr loop.

110           buttonView.setChecked(false);

111       }

112

113       // Disable tethering if enabling Wifi

114       int wifiApState = mWifiManager.getWifiApState();

115       if (isChecked && ((wifiApState ==WifiManager.WIFI_AP_STATE_ENABLING) ||

116               (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {

117           mWifiManager.setWifiApEnabled(null, false);

118       }

119

120       if (mWifiManager.setWifiEnabled(isChecked)) {

121           // Intent has been taken into account, disable until new state isactive

122           mSwitch.setEnabled(false);

123       } else {

124           // Error

125           Toast.makeText(mContext, R.string.wifi_error,Toast.LENGTH_SHORT).show();

126       }

127    }

可以看出当不是AP时,是使能Wifi,默认行为是根据配置挑选出AP进行连接。

另外的两个类的解释:

WifiConfigController是MVC中的Controller

WifiDialog是单击的config对话框

连接和DHCP过程中显示的字符串xml文件如下:

In /packages/apps/Settings/res/values/arrays.xml

223   

224

225   

226   

227   

228       

229       

230       

231       Scanning\u2026

232       

233       Connecting\u2026

234       

235       Authenticating\u2026

236       

237       Obtaining IP address\u2026

238       

239       Connected

240       

241       Suspended

242       

243       Disconnecting\u2026

244       

245       Disconnected

246       

247       Unsuccessful

248   

至此UI界面响应部分介绍完毕,下面看WifiManager.

WifiManager是WifiService的客户端接口的封装类,WifiService是服务实现,接口是IWifiManager。

In IWifiManager.aidl

32interface IWifiManager {};

InWifiService.java   

public class WifiService extends IWifiManager.Stub {};

 

In WifiManager.java, NOT the intermmediate file ofIWifiManager.aidl

485    publicWifiManager(IWifiManager service, Handler handler) {

486       mService = service;

487       mHandler = handler;

488    }

InContextImpl.java每个Activity关联的Contect会得到WIFI_SERVICE,然后构造WifiManager封装类。代码如下:

449       registerService(WIFI_SERVICE, new ServiceFetcher() {

450               public Object createService(ContextImpl ctx) {

451                  IBinder b = ServiceManager.getService(WIFI_SERVICE);

452                   IWifiManager service = IWifiManager.Stub.asInterface(b);

453                   returnnew WifiManager(service, ctx.mMainThread.getHandler());

454               }});

 

几个同步操作的控制流示例:

WifiSettings=>WifiManager::reconnect(…)()||||

=>WifiService:: reconnect ()=>WifiStateMachine ::reconnectCommand()

Scanner=>WifiManager::startScanActive()|||| =>WifiService::startScan(active)=>WifiStateMachine::startScan(active)

WifiEnabler=> WifiManager::setWifiEnabled()|||| =>WifiService::setWifiEnabled()=>WifiStateMachine::setWifiEnabled()

WifiManager中大部分同步命令都是这么个流程;耗时的异步命令则是通过AsyncChannel发送给WifiService.

 

WifiService Part structure
InWifiService.java   

public class WifiService extends IWifiManager.Stub {};

WifiService线程的启动如下

In SystemServer.java

384          try {

385               Slog.i(TAG, "Wi-Fi P2pService");

386               wifiP2p = new WifiP2pService(context);

387               ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p);

388           } catch (Throwable e) {

389               reportWtf("starting Wi-Fi P2pService", e);

390           }

391

392          try {

393               Slog.i(TAG, "Wi-Fi Service");

394               wifi = new WifiService(context);

395               ServiceManager.addService(Context.WIFI_SERVICE, wifi);

396           } catch (Throwable e) {

397               reportWtf("starting Wi-Fi Service", e);

398           }

WifiService的构造函数本质是启动了一个带有消息队列的线程做WifiService,两个Handlerattach到该消息队列上。

428       HandlerThread wifiThread = new HandlerThread("WifiService");

429       wifiThread.start();

430       mAsyncServiceHandler = newAsyncServiceHandler(wifiThread.getLooper());

431       mWifiStateMachineHandler = newWifiStateMachineHandler(wifiThread.getLooper());

AsyncServiceHandler做为destHandler用于处理下行的来自WifiManager客户端的命令消息,而WifiStateMachineHandler做为srcHandler用于向WifiStateMachine.mSmhandler发送异步命令。现在的WifiStatemachine实现是和WifiService使用同一个looper,在同一个线程中,所以mAsyncServiceHandler、mWifiStateMachineHandler、WifiStateMachine.mSmhandler是使用同一个looper,运行在wifiThread中。WifiStateMachine籍由StateMachine具备有单独looper在单独线程运行的能力。

WifiSerivce作为一个service,WifiService.java在frameworks/base/services/java/com/android/server/目录下。但其实现使用的WifiStateMachine在frameworks/base/wifi/java/android/net/wifi/目录下。

WifiStateMachine状态机使用WifiNative wpa_ctrl和wpa_supplicant通讯,WifiMonitor监听wpa_supplicant的异步消息。因为WifiStateMachine的核心是状态机,所以其接口都是转换成命令投入状态机消息队列。WifiService传来的命令和WifiMonitor监听到的响应汇入WifiStateMachine的消息队列,驱动WifiStateMachine转动起来。

WifiStateMachine继承自StateMachine,是一个hierarchical state machine whichprocesses messages and can have states arrangedhierarchically,其细节参见StateMachine.java.WifiStateMachine的hierarchy如下图所示:

 

下面考察初始状态到Wifi使能扫描AP连接AP查询AP过程的状态机运转。

In WifiStateMachine.java

状态机会启动,进入InitialState状态。然后UI打开WifiEnabler最终WifiService::setWifiEnabled会被执行到。相关代码如下:

553    publicWifiStateMachine(Context context, String wlanInterface) {

649       if (DBG) setDbg(true);

650

651       //start the state machine

652       start();

653    }

680    publicvoid setWifiEnabled(boolean enable) {

681       mLastEnableUid.set(Binder.getCallingUid());

682       if (enable) {

683           

684           sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING,0));

685           sendMessage(CMD_START_SUPPLICANT);

686       } else {

687           sendMessage(CMD_STOP_SUPPLICANT);

688           

689           sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED,0));

690       }

691    }

 

在InitialState.enter()中,初始情况下WifiDriver是没有加载的,所以进入DriverUnloadedState状态;

然后setWifiEnabled向状态机发出CMD_LOAD_DRIVER withWIFI_STATE_ENABLING和CMD_START_SUPPLICANT两个命令。

在DriverUnloadedState状态下,处理CMD_LOAD_DRIVER命令,向WifiP2pService发送WIFI_ENABLE_PENDING命令,也就是说通知WifiP2pService需要退出要启用Wifi了,因为WifiService和WifiP2pService同一时刻只能使用一个,然后转到WaitForP2pDisableState状态等待WifiP2pService的响应。

在WaitForP2pDisableState状态下收到WifiP2pService的响应WIFI_ENABLE_PROCEED,转到DriverLoadingState状态加载Wifidriver;对于其它请求会deferMessage到以后状态处理。

在DriverLoadingState状态启用线程异步加载Wifidriver,当加载成功时,向状态机自身发送消息CMD_LOAD_DRIVER_SUCCESS驱动状态机转换到DriverLoadedState,加载失败则发送消息CMD_LOAD_DRIVER_FAILED驱动状态机转换到DriverFailedState状态(会经由DriverUnloadedState)。

在DriverLoadedState状态下处理setWifiEnabled发出的defer到此的第二个命令CMD_START_SUPPLICANT,加载网卡芯片固件,启动wpa_supplicant,启动WifiMonitor接收wpa_supplicant的消息,转换状态到SupplicantStartingState等待WifiMonitor的事件(wpa_supplicant接入成功事件)。

在SupplicantStartingState状态下收到WifiMonitor.SUP_CONNECTION_EVENT表接入wpa_supplicant成功,regulate国家代号,初始化一些表征wpa_s的字段,转换状态至DriverStartedState;对于其它命令,defer处理。

经SupplicantStartedState.enter()和DriverStartedState.enter()中使用CMD_SET_COUNTRY_CODE和CMD_SET_FREQUENCY_BAND设置国家代号和频段,在本状态处理这两个命令;这两个命令是要发送给芯片的,所以在固件和驱动加载好后发送。设置好频段后,就向自身发送消息CMD_START_SCAN启动一次activescan。然后经由状态ConnectModeState转换状态至DisconnectedState。

在DisconnectedState下收到扫描结果WifiMonitor.SCAN_RESULTS_EVENT,消息路由给父状态SupplicantStartedState的processMessage进行处理,获取扫描结果并保存。在此状态下SupplicantStartedState处理与AP配置相关的命令,ConnectModeState处理与AP连接相关的命令。例如可以配置某个AP,然后请求connectNetwork,对于CMD_CONNECT_NETWORK,会使用Connect相关状态ConnectModeState.processMessage()处理,启动AP关联,然后转换状态到DisconnectingState与旧AP断开连接与新AP建立连接。

在DisconnectingState状态收到关联到新AP成功消息WifiMonitor.NETWORK_CONNECTION_EVENT后,使用ConnectModeState.processMessage处理,记录关联上的SSID,BSSID,获取AP信号强度和速度,广播消息,转换状态至ConnectingState进行IP获取。

在ConnectingState.enter()中使用静态IP或DHCP获取IP,配置IP,成功则转入ConnectedState状态。

在ConnectedState状态下,可以处理配置AP、连接新的AP、断开连接等,还有扫描请求,另外还要查询当前连接AP的信号强度和网络流量信息,在状态栏显示Wifi状态图标。这时通过CMD_RSSI_POLL命令实现的,当WifiService构造时,允许enableRssiPolling,一次CMD_ENABLE_RSSI_POLL会引发定时的CMD_RSSI_POLL,当屏幕未关闭时,CMD_RSSI_POLL就会定时向自身状态机发送;然后ConnectedState.processMessage调用fetchRssiAndLinkSpeedNative()处理CMD_RSSI_POLL。

0 0
原创粉丝点击