本文介紹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