Android WIFI 分析(二)

本文介紹Wifi 分析線路二:在Setting中打開WiFi功能、掃描網絡以及鏈接網絡的流程。安全

WifiSettings 無線網絡設置界面網絡

WifiEnabler 至關於無線網絡設置開關ide

WifiDialog 顯示的無線網絡配置信息由WifiConfigController 來控制和管理函數

Scanner 用於處理和無線網絡掃描相關的工做oop

 

一、Settings 操做fetch

無線網絡設置界面UI 初始化過程當中,WifiSettings 的onActivityCreated() 方法被調用:ui

    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mWifiTracker =
                new WifiTracker(getActivity(), this, mBgThread.getLooper(), true, true, false);
        mWifiManager = mWifiTracker.getManager();
    }

WifiTracker 構造函數調用:this

    WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
            boolean includeSaved, boolean includeScans, boolean includePasspoints,
            WifiManager wifiManager, ConnectivityManager connectivityManager,
            Looper currentLooper) {
     //添加多個廣播事件,在startTracker() 方法中進行註冊
        mFilter = new IntentFilter();
        mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
        mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
        mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
        mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
        mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);

        mNetworkRequest = new NetworkRequest.Builder()
                .clearCapabilities()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .build();
    }

WifiSettings 的onStart() 方法建立WifiEnabler:spa

    public void onStart() {
        // On/off switch is hidden for Setup Wizard (returns null)
        mWifiEnabler = createWifiEnabler();
    }

     WifiEnabler createWifiEnabler() {
        final SettingsActivity activity = (SettingsActivity) getActivity();
        return new WifiEnabler(activity, activity.getSwitchBar());
    }

WifiEnabler 的構造函數,添加廣播事件:code

    public WifiEnabler(Context context, SwitchBar switchBar) {
        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
     //添加三個廣播事件
        mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
        // The order matters! We really should not depend on this. :(
        mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);

        setupSwitchBar();
    }

WIFI_STATE_CHANGED_ACTION:反映WiFi 功能所對應的狀態,包括WIFI_STATE_DISABLED(Wifi 功能已被關閉)、WIFI_STATE_DISABLING(Wifi 功能正在關閉中)、WIFI_STATE_ENABLED(Wifi 功能已被打開)、WIFI_STATE_ENABLING(Wifi 功能正在打開中)、WIFI_STATE_UNKNOWN(Wifi 功能狀態未知)。

SUPPLICANT_STATE_CHANGED_ACTION:表示WPAS 的狀態發生了變化。

NETWORK_STATE_CHANGED_ACTION:表示WIFI 鏈接狀態發生變化,其攜帶的信息通常是NetworkInfo 對象。

 

WifiSettings 和 WifiEnabler 的設置的廣播接收對象在onResume() 的方法中被註冊:

    public void onResume() {
        final Activity activity = getActivity();
        if (mWifiEnabler != null) {
            mWifiEnabler.resume(activity); //調用WifiEnabler 類
        }

        mWifiTracker.startTracking(); //調用framework WifiTracker 類
        activity.invalidateOptionsMenu();
    }

WifiEnabler 類的resume() 方法以下:

    public void resume(Context context) {
        mContext = context;
        // Wi-Fi state is sticky, so just let the receiver update UI
        mContext.registerReceiver(mReceiver, mIntentFilter); //註冊構造函數中添加的三個廣播事件
        if (!mListeningToOnSwitchChange) {
            mSwitchBar.addOnSwitchChangeListener(this);
            mListeningToOnSwitchChange = true;
        }
    }

WifiTracker 類的startTracking() 方法以下:

    /**
     * Start tracking wifi networks.
     * Registers listeners and starts scanning for wifi networks. If this is not called
     * then forceUpdate() must be called to populate getAccessPoints().
     */
    public void startTracking() {
        resumeScanning();
        if (!mRegistered) {
            mContext.registerReceiver(mReceiver, mFilter); //註冊構造函數中添加的廣播事件
            // NetworkCallback objects cannot be reused. http://b/20701525 .
            mNetworkCallback = new WifiTrackerNetworkCallback();
            mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
        }
    }

WifiEnabler 處理較多的及時WIFI_STATE_CHANGED_ACTION 廣播,根據此廣播信息更新Switch 的界面。

 

二、啓用WIFI 功能

WifiEnabler 實現了SwitchBar 的onSwitchChangeListener 接口,故用戶點擊事件將觸發WifiEnabler 的onSwitchChanged() 方法:

    public void onSwitchChanged(Switch switchView, boolean isChecked) {      
        // Disable tethering if enabling Wifi
        if (mayDisableTethering(isChecked)) {
            mWifiManager.setWifiApEnabled(null, false);
        }
    }

WifiManager 的setWifiApEnabled() 方法將觸發WifiService 開展一系列的動做,在此過程當中,WifiManager 會經過發送廣播的方法向外界發佈一些信息,因此需重點關注廣播事件的處理。

WifiTracker 的廣播接收對象的處理:

    final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
                updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                        WifiManager.WIFI_STATE_UNKNOWN));
            } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) || WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
                mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS); //發送更新無線網絡列表消息
            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
                NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                        WifiManager.EXTRA_NETWORK_INFO);
                mConnected.set(info.isConnected());
                mMainHandler.sendEmptyMessage(MainHandler.MSG_CONNECTED_CHANGED);
                mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
                mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO, info)
                        .sendToTarget();
            }
        }
    };

WifiEnabler 的廣播接收對象的處理:

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
                handleWifiStateChanged(intent.getIntExtra(
                        WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN));
            } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
                if (!mConnected.get()) {
                    handleStateChanged(WifiInfo.getDetailedStateOf((SupplicantState)
                            intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
                }
            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
                NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                        WifiManager.EXTRA_NETWORK_INFO);
                mConnected.set(info.isConnected());
                handleStateChanged(info.getDetailedState());
            }
        }
    };

1) 觸發掃描

當WIFI 功能被啓用時,將收到WIFI_STATE_CHANGED_ACTION 廣播,該廣播的處理函數是WifiTracker 的updateWifiState() 方法:

    private void updateWifiState(int state) {
        mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_WIFI_STATE, state, 0).sendToTarget();
    } //向內部WrokHandler 對象發送消息

MSG_UPDATE_WIFI_STATE 的處理:

                case MSG_UPDATE_WIFI_STATE:
                    if (msg.arg1 == WifiManager.WIFI_STATE_ENABLED) {
                        if (mScanner != null) {
                            // We only need to resume if mScanner isn't null because
                            // that means we want to be scanning.
                            mScanner.resume(); //啓動掃描
                        }
                    } else {
                        mLastInfo = null;
                        mLastNetworkInfo = null;
                        if (mScanner != null) {
                            mScanner.pause();
                        }
                    }
                    mMainHandler.obtainMessage(MainHandler.MSG_WIFI_STATE_CHANGED, msg.arg1, 0)
                            .sendToTarget(); //向內部MainHandler 對象發送消息
                    break;

Scanner 也是WifiTracker 內部定義的一個Handler:

    class Scanner extends Handler {
        static final int MSG_SCAN = 0;

        void resume() {
            if (!hasMessages(MSG_SCAN)) {
                sendEmptyMessage(MSG_SCAN);
            }
        }

        @Override
        public void handleMessage(Message message) {
            if (message.what != MSG_SCAN) return;
            if (mWifiManager.startScan()) { //發起掃描
                mRetry = 0;
            } else if (++mRetry >= 3) { //掃描失敗
                mRetry = 0;
                if (mContext != null) {
                    Toast.makeText(mContext, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
                }
                return;
            }
            sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS); //每1秒發起一次掃描
        }
    }

2) 更新AP 列表

若是WPAS 掃描完畢,則WifiTracker 將收到SCAN_RESULTS_AVAILABLE_ACTION 廣播,發送消息MSG_UPDATE_ACCESS_POINTS,WorkHandler 對此消息的處理以下:

                case MSG_UPDATE_ACCESS_POINTS:
                    updateAccessPoints();
                    break;

繼而調用updateAccessPoints() 方法:

    private void updateAccessPoints() {
        final Collection<ScanResult> results = fetchScanResults(); //獲取掃描結果
        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
        // Pre-sort accessPoints to speed preference insertion
        Collections.sort(accessPoints); //建立AP 列表
        mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED); //向MainHandler 發送MSG_ACCESS_POINT_CHANGED

3) 加入目標無線網絡

從列表中選擇加入某個無線網絡,處理用戶選擇AP 事件的方法是WifiSettings 的onPreferenceTreeClick() 方法:

    public boolean onPreferenceTreeClick(Preference preference) {
        if (preference instanceof LongPressAccessPointPreference) {
            mSelectedAccessPoint = ((LongPressAccessPointPreference) preference).getAccessPoint();
            /** Bypass dialog for unsecured, unsaved, and inactive networks */
            if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE &&
                    !mSelectedAccessPoint.isSaved() && !mSelectedAccessPoint.isActive()) { //對於沒有安全設置的無線網絡,直接鏈接它便可
                mSelectedAccessPoint.generateOpenNetworkConfig();
                connect(mSelectedAccessPoint.getConfig());
            } else if (mSelectedAccessPoint.isSaved()) {
                showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_VIEW); //彈出無線密碼輸入框
            } else {
                showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
            }
        } else if (preference == mAddPreference) {
            onAddNetworkPressed();
        } 
    }

當用戶設置完目標無線網絡(例如設置密碼)後,點擊「鏈接」按鈕,將觸發WifiSettings 的submit() 方法被調用:

    /* package */ void submit(WifiConfigController configController) {
        final WifiConfiguration config = configController.getConfig();
        if (config == null) {
        ......
        } else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) {
            mWifiManager.save(config, mSaveListener);
        } else {
            mWifiManager.save(config, mSaveListener);
            if (mSelectedAccessPoint != null) { // Not an "Add network"
                connect(config); //鏈接目標無線網絡
            }
        }
        mWifiTracker.resumeScanning();
    }

至此,後續的工做就是等待並處理廣播事件,若是一切順利,將接收一個NETWORK_STATE_CHANGED_ACTION 廣播事件以告知手機成功加入目標無線網絡。

和WifiManager 交互的幾個重要函數以做下面分析重點:

setWifiEnabled():啓用WIFI 功能

startScanActive():啓動AP 掃描

connect():鏈接至目標AP

相關文章
相關標籤/搜索