Android Wifi work station Framework and Architecture

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<ScanResult> 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<AccessPoint> 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) {

                    /** Bypass dialog for unsecured networks */

                    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            /** Bypass dialog for unsecured, unsaved networks */

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    <!– Wi-Fi settings –>

224

225    <!– Match this with the order of NetworkInfo.DetailedState. –> <skip />

226    <!– Wi-Fi settings. The status messages when the network is unknown. –>

227    <string-array name="wifi_status">

228        <!– Status message of Wi-Fi when it is idle. –>

229        <item></item>

230        <!– Status message of Wi-Fi when it is scanning. –>

231        <item>Scanning\u2026</item>

232        <!– Status message of Wi-Fi when it is connecting. –>

233        <item>Connecting\u2026</item>

234        <!– Status message of Wi-Fi when it is authenticating. –>

235        <item>Authenticating\u2026</item>

236        <!– Status message of Wi-Fi when it is obtaining IP address. –>

237        <item>Obtaining IP address\u2026</item>

238        <!– Status message of Wi-Fi when it is connected. –>

239        <item>Connected</item>

240        <!– Status message of Wi-Fi when it is suspended. –>

241        <item>Suspended</item>

242        <!– Status message of Wi-Fi when it is disconnecting. –>

243        <item>Disconnecting\u2026</item>

244        <!– Status message of Wi-Fi when it is disconnected. –>

245        <item>Disconnected</item>

246        <!– Status message of Wi-Fi when it is a failure. –>

247        <item>Unsuccessful</item>

248    </string-array>

至此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            /* Argument is the state that is entered prior to load */

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

685            sendMessage(CMD_START_SUPPLICANT);

686        } else {

687            sendMessage(CMD_STOP_SUPPLICANT);

688            /* Argument is the state that is entered upon success */

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。

 

You May Also Like