Android Wifi work&nbsp…

Android Wifi work station Framework and Architecture

with wpa_supplicant 0.8.X, BCM4329.

转载请注明出处。

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

167

168    @Override

169    public void onActivityCreated(Bundle savedInstanceState) {

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

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

172        // this method.

173

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

175       mWifiManager.asyncConnect(getActivity(), new WifiServiceHandler());

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    public void 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做event sink,又有点小题大做,所以这儿引入这么个AsyncChannel实在是不得以而为之。

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

    private class 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 void updateAccessPoints() {

        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

    public boolean 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    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {

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    public void 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    public void 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 is active

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 {};

In WifiService.java   

public class WifiService extends IWifiManager.Stub {};

 

In WifiManager.java, NOT the intermmediate file of IWifiManager.aidl

485    public WifiManager(IWifiManager service, Handler handler) {

486        mService = service;

487        mHandler = handler;

488    }

In ContextImpl.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
In WifiService.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,两个Handler attach到该消息队列上。

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

429        wifiThread.start();

430        mAsyncServiceHandler = new AsyncServiceHandler(wifiThread.getLooper());

431        mWifiStateMachineHandler = new WifiStateMachineHandler(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 which processes messages and can have states arranged hierarchically,其细节参见StateMachine.java. WifiStateMachine的hierarchy如下图所示:

 

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

In WifiStateMachine.java

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

553    public WifiStateMachine(Context context, String wlanInterface) {

649        if (DBG) setDbg(true);

650

651        //start the state machine

652        start();

653    }

680    public void 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 with WIFI_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状态加载Wifi driver;对于其它请求会deferMessage到以后状态处理。

在DriverLoadingState状态启用线程异步加载Wifi driver,当加载成功时,向状态机自身发送消息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启动一次active scan。然后经由状态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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值