Android提供了多种网络链接通道,最常见的是WIFI和移动基站通信(Cellular),另外还可将终端本身作为热点(Wifi Access Point),也可通过WIFI进行将两个终端直接进行连接(Wifi P2P),从而交互数据;同时还支持蓝牙将WIFI作为数据热点(Access Point),为其他设备提供网络接入。
对于移动通信,不同的运营商提供了不同的服务,网络提供的服务能力也各不相同。那么,Android是如何区分网络的承载能力的?为了区分不同网络的承载能力,Android提供了多达20种网络能力用以区分网络数据的接入方式(以下只列举常见的类型):
ConnectivityManager常用接口
ConnectivityManager提供了很多接口用于获取系统当前的网络连接信息:
它主要涉及的类有TetherSettings.java 、WifiApEnabler.java、WifiStateMachine.java以及它的布局文件tether_prefs.xml
首先从布局文件tether_prefs.xml分析
key="enable_wifi_ap"就是代表热点开关,根据key值我们转到布局文件所在的类TetherSettings.java。
初始化
addPreferencesFromResource(R.xml.tether_prefs);
...
mEnableWifiAp =(SwitchPreference) findPreference(ENABLE_WIFI_AP);
监听开关状态改变
private WifiApEnabler mWifiApEnabler;
//将开关和WifiApEnabler绑定,在WifiApEnabler中进行开关的状态更新
mWifiApEnabler = new WifiApEnabler(activity, mDataSaverBackend, mEnableWifiAp);
@Override
public void onStart() {
...
if (mWifiApEnabler != null) {
mEnableWifiAp.setOnPreferenceChangeListener(this);
mWifiApEnabler.resume();
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
boolean enable = (Boolean) value;
if (enable) {
startTethering(TETHERING_WIFI);
} else {
mCm.stopTethering(TETHERING_WIFI);
}
return false;
}
继续跟踪,热点状态改变
private class TetherChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context content, Intent intent) {
String action = intent.getAction();
if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
...
startTethering(TETHERING_WIFI);
} else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0);
if (state == WifiManager.WIFI_AP_STATE_DISABLED
&& mRestartWifiApAfterConfigChange) {
mRestartWifiApAfterConfigChange = false;
Log.d(TAG, "Restarting WifiAp due to prior config change.");
startTethering(TETHERING_WIFI);
}
}
...//热点状态变化/连接方式参数变化都会导致startTethering()
//顺便更新USB和BT连接的状态
}
有接收就有发送,发送广播的是WifiStateMachine.java,它在frameworks目录下
private void setWifiApState(int wifiApState, int reason, String ifaceName, int mode) {
final int previousWifiApState = mWifiApState.get();
...
final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
if (wifiApState == WifiManager.WIFI_AP_STATE_FAILED) {
//only set reason number when softAP start failed
intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);
}
intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, ifaceName);
intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mode);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
另外,在设置的dialog中获取用户设置的信息
public WifiConfiguration getConfig() {
WifiConfiguration config = new WifiConfiguration();
/**
* TODO: SSID in WifiConfiguration for soft ap
* is being stored as a raw string without quotes.
* This is not the case on the client side. We need to
* make things consistent and clean it up
*/
config.SSID = mSsid.getText().toString();
config.apBand = mBandIndex;
switch (mSecurityTypeIndex) {
case OPEN_INDEX:
config.allowedKeyManagement.set(KeyMgmt.NONE);
return config;
case WPA2_INDEX:
config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
if (mPassword.length() != 0) {
String password = mPassword.getText().toString();
config.preSharedKey = password;
}
return config;
}
return null;
}
在TetherSettings.java中实现保存并生效
public void onClick(DialogInterface dialogInterface, int button) {
if (button == DialogInterface.BUTTON_POSITIVE) {
mWifiConfig = mDialog.getConfig();
if (mWifiConfig != null) {
/**
* if soft AP is stopped, bring up
* else restart with new config
* TODO: update config on a running access point when framework support is added
*/
if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
Log.d("TetheringSettings",
"Wifi AP config changed while enabled, stop and restart");
mRestartWifiApAfterConfigChange = true;
mCm.stopTethering(TETHERING_WIFI);
}
mWifiManager.setWifiApConfiguration(mWifiConfig);
int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig);
mCreateNetwork.setSummary(String.format(getActivity().getString(CONFIG_SUBTEXT),
mWifiConfig.SSID,
mSecurityType[index]));
}
}
}