Android4.4.2原始碼分析之WiFi模組(一)
阿新 • • 發佈:2019-01-08
已經寫了幾篇關於Android原始碼的,原始碼程式碼量太大,所以如果想分析某個模組可能不知如何下手,說一下思路
1,分析原始碼英文閱讀能力要夠,想要分析某個模組一般找模組對應的英文,就是模組
2,找到之後首先檢視清單配置檔案Androidmani.fest,找到程式主介面activity
3,通過檢視配置檔案中的許可權可以知道應用都有什麼用
大致通過以上三步就可以進入原始碼進行分析
針對WiFi,我們通過清單檔案可以知道,需要以下幾個許可權,其含義如下注釋
<!-- 允許程式獲取WiFi狀態 --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <!--允許程式改變WiFi狀態--> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <!--允許程式獲取手機網路狀態--> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <!--允許程式改變網路狀態--> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> <!--允許程式訪問網路--> <uses-permission android:name="android.permission.INTERNET"/>
如果使用as做開發工具的話在使用WiFi時如果不加許可權會有警告來提示你,感覺還是很人性化的,如果把as的所有警告都解決了那麼程式應該會很少出意料之外的bug,而且as的單步除錯可以很好的去發現bug,介面美觀,忍不住強烈推薦。
因為是首次接觸WiFi原始碼,我會按程式碼順序分析,當然這樣可能會有些亂,完成全部分析後會再按照功能點分析,不過得等到下一篇部落格嘍。好了,閒話少敘,正式進入對WiFi原始碼的分析,
1,WifiSettings屬於fragment,實現了對話方塊的點選事件介面,程式碼如下
public class WifiSettings extends RestrictedSettingsFragment implements DialogInterface.OnClickListener
在onCreateView方法中首先判斷應用是否是第一次執行,根據是否是第一次執行來設定不同的佈局
1>,if(mSetupWizardMode) 則載入啟動佈局
View view = inflater.inflate(R.layout.setup_preference, container, false);
否則載入正常佈局
View v = inflater.inflate(R.layout.add_preference_list_fragment,null);
接著onActivityCreated方法中
2>
mP2pSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);
這句程式碼用來判斷是否支援WiFi直連:if the device supports Wi-Fi Direct networking or not
WiFi直連的含義是說:裝置可以直接通過WiFi共享檔案,圖片等,類似藍芽傳輸,實現點對點的連線,通訊(要求兩裝置必須位於同一網段)。可以看到判斷裝置是否支援WiFi直連是通過PackageManager的hasSystemFeature
目錄為/android/external/robolectric/src/main/java/com/xtremelabs/robolectric/res/
public boolean hasSystemFeature(String name) {
return systemFeatureList.containsKey(name) ? systemFeatureList.get(name) : false;
}
由原始碼可知該方法會遍歷systemFeaturelist列表,判斷是否包含所傳入的feature,通過PackageManagerService中的readPermissionsFromXml(File f)傳入xml檔案進行解析獲取到裝置所支援的permission
3>,接下來獲取到WiFi的管理類物件mWifiManager,用於開啟/關閉WiFi,掃描WiFi,連線WiFi等等
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
4>,通過獲取到set_wifi_priority的值來對preference列表進行 不同顯示
<pre name="code" class="java">if (getResources().getBoolean(R.bool.set_wifi_priority)) {
addPreferencesFromResource(R.xml.wifi_sort_settings);
mDefaultTrustAP
= (PreferenceCategory)findPreference("default_trust_access_points");
mConfigedAP = (PreferenceCategory)findPreference("configed_access_points");
mUnKnownAP = (PreferenceCategory)findPreference("unknown_access_points");
}
else {
addPreferencesFromResource(R.xml.wifi_settings);
}
對於boolean值“set_wifi_priority”的值可以檢視Z:\L7-A1\android\packages\apps\Settings\res\values\bools.xml檔案該值表示whether
to show hotspot via the ap's classification接入點優先順序設定即是否按照所搜尋到的WiFi優先順序來顯示Wifi列表,可以看到等級有三種:預設的信任接入點,信任接入點,未知接入點因為在這裡boolean值被設定為false,所以不按優先順序排序5>,接下來就是新增WiFi開關,至於通過程式碼在導航欄actionbar新增switch的程式碼類似藍芽,在此不再贅述,可以看到,藍芽開關switch傳入了WifiEnabler,所以對於Wifi的開關的管理位於WiFiEnablerswitch中mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);
進入WiFiEnabler可以看到,該類就做了兩件事,第一註冊廣播監聽WiFi的變化並隨之改變switch的狀態,第二為switch新增點選監聽事件
廣播所監聽的事件有
//當WiFi狀態發生改變時會發送該廣播
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);
action WifiManager.SUPPLICANT_STATE_CHANGD_ACTION定義如下,表示正在建立連線的狀態發生改變,又有了新的連線可用,可用獲取到WiFi的具體的連線狀態,如果你只是對連線的整體狀態感興趣則該廣播無用
/**
* Broadcast intent action indicating that the state of establishing a connection to
* an access point has changed.One extra provides the new
* {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and
* is not generally the most useful thing to look at if you are just interested in
* the overall state of connectivity.
* @see #EXTRA_NEW_STATE
* @see #EXTRA_SUPPLICANT_ERROR
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String SUPPLICANT_STATE_CHANGED_ACTION =
"android.net.wifi.supplicant.STATE_CHANGE";
接下來看廣播的處理
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)) {
//WiFi狀態生改變時去更新switch的狀態,WiFi的狀態存在於WifiManager.EXTRA_WIFI_STATE
handleWifiStateChanged(intent.getIntExtra(
WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN));
//needPrompt方法用於判斷是否是飛航模式以及飛航模式是否無效
if (WifiSettings.needPrompt(context)) {
setSwitchChecked(false);
}
} else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
//連線發生改變時的更新,WifiManager.EXTRA_NEW_STATE存放改變後的狀態
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());
}
}
};
當WiFi開關狀態發生改變時更新放啊如下:
private void handleWifiStateChanged(int state) {
switch (state) {
case WifiManager.WIFI_STATE_ENABLING://正在開啟WiFi
mSwitch.setEnabled(false);
break;
case WifiManager.WIFI_STATE_ENABLED://WiFi已經開啟
setSwitchChecked(true);
mSwitch.setEnabled(true);
break;
case WifiManager.WIFI_STATE_DISABLING://正在關閉WiFi
mSwitch.setEnabled(false);
break;
case WifiManager.WIFI_STATE_DISABLED://WiFi已經關閉
setSwitchChecked(false);
mSwitch.setEnabled(true);
break;
default:
setSwitchChecked(false);
mSwitch.setEnabled(true);
break;
}
}
對於網路狀態發生改變時的更新方法,可以看到方法已經被註釋掉,因為不需要去更新preference的副標題
private void handleStateChanged(@SuppressWarnings("unused") NetworkInfo.DetailedState state) {
// After the refactoring from a CheckBoxPreference to a Switch, this method is useless since
// there is nowhere to display a summary.
// This code is kept in case a future change re-introduces an associated text.
/*
// WifiInfo is valid if and only if Wi-Fi is enabled.
// Here we use the state of the switch as an optimization.
if (state != null && mSwitch.isChecked()) {
WifiInfo info = mWifiManager.getConnectionInfo();
if (info != null) {
//setSummary(Summary.get(mContext, info.getSSID(), state));
}
}
*/
}
對於switch的點選事件的監聽程式碼如下
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//Do nothing if called as a result of a state machine event
if (mStateMachineEvent) {
return;
}
if (mContext.getResources().getBoolean(R.bool.wifi_to_cell)) {
ConnectivityManager mConnService = (ConnectivityManager) mContext.
getSystemService(Context.CONNECTIVITY_SERVICE);
if (mConnService != null) {
NetworkInfo netInfo = (NetworkInfo) mConnService
.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (netInfo != null && netInfo.isConnected()) {
Settings.System.putInt(mContext.getContentResolver(), WIFI_IS_CONNECTED,
CONNECTED);
} else {
Settings.System.putInt(mContext.getContentResolver(), WIFI_IS_CONNECTED,
DISCONNECTED);
}
}
}
// Show toast message if Wi-Fi is not allowed in airplane mode
//判斷是否在飛航模式中WiFi是不允許的
if (isChecked
&& (WifiSettings.needPrompt(mContext) || !WirelessSettings.isRadioAllowed(
mContext, Settings.Global.RADIO_WIFI))) {
Toast.makeText(mContext, R.string.wifi_in_airplane_mode,
Toast.LENGTH_SHORT).show();
// Reset switch to off. No infinite check/listenenr loop.
buttonView.setChecked(false);
return;
}
// Disable tethering if enabling Wifi
int wifiApState = mWifiManager.getWifiApState();
if (isChecked && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
(wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
mWifiManager.setWifiApEnabled(null, false);
}
// shouldn't setWifiEnabled(true) in airplane mode.
if (isChecked && WifiSettings.needPrompt(mContext)) {
return;
} else {
//setWifiEnabled開啟或者關閉WiFi的方法,會發送WiFi狀態改變的廣播:WIFI_STATE_CHANGED_ACTION
if (mWifiManager.setWifiEnabled(isChecked)) {
// Intent has been taken into account, disable until new state
// is active
mSwitch.setEnabled(false);
} else {
// Error
Toast.makeText(mContext, R.string.wifi_error,
Toast.LENGTH_SHORT).show();
}
}
}<span style="font-size:14px;">
</span>
對於mStateMachineEvent的值是在對switch進行設定時賦值,起開關保護的作用,保證當點選switch的時候先將switch狀態設定成功再進入點選事件方法private void setSwitchChecked(boolean checked) {
if (checked != mSwitch.isChecked()) {
mStateMachineEvent = true;
mSwitch.setChecked(checked);
mStateMachineEvent = false;
}
}
開啟或關閉WiFi的方法為,mWifiManager.setWifiEnabled(boolean enable),在開啟時會發送廣播WifiManager.WIFI_STATE_CHANGED_ACTION,可監聽該廣播來改變UI