基于Android 9.0 ATV版 TVSetting源码,研究TVSetting Wifi连接方法
一、概要
这篇主要是对WifiTracker分析
WifiTracker不单是提供TVSetting,而是作为一个公共类,由com.android.settingslib提供给TVSetting和Setting使用。WifiTracker通过Lifecycle和一个WifiListener实现与调用者的交互
1.1 WifiTracker观察LifecycleOwner的状态
WifiTracker WifiTracker实现了LifecycleObserver观察者接口,用来观察Fragment的状态。
主要观察了
onStart事件
onStop事件
1.2 WifiTracker实现了一个内部接口WifiListener,用于在监控到wifi信号发生改变后,通知UI更新
public interface WifiListener {
/**
* Called when the state of Wifi has changed, the state will be one of
* the following.
*
* <li>{@link WifiManager#WIFI_STATE_DISABLED}</li>
* <li>{@link WifiManager#WIFI_STATE_ENABLED}</li>
* <li>{@link WifiManager#WIFI_STATE_DISABLING}</li>
* <li>{@link WifiManager#WIFI_STATE_ENABLING}</li>
* <li>{@link WifiManager#WIFI_STATE_UNKNOWN}</li>
* <p>
*
* @param state The new state of wifi.
*/
void onWifiStateChanged(int state);
/**
* Called when the connection state of wifi has changed and
* {@link WifiTracker#isConnected()} should be called to get the updated state.
*/
void onConnectedChanged();
/**
* Called to indicate the list of AccessPoints has been updated and
* {@link WifiTracker#getAccessPoints()} should be called to get the updated list.
*/
void onAccessPointsChanged();
}
1.3 WifiTracker关键类及构造函数
WifiTracker几个关键类
➢mWifiManager-管理wifi连接
➢mConnectivityManager-管理网络连接状态
➢mNetworkScoreManager-打分管理,资料太少了,需要进一步研究
➢AccessPoint--wifi节点管理类
WifiTracker要求调用者实现WifiListener,LifecycleOwner,并作为构造参数传入
public WifiTracker(Context context, WifiListener wifiListener,
@NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
//调用最终的构造函数
this(context, wifiListener,
context.getSystemService(WifiManager.class),
context.getSystemService(ConnectivityManager.class),
context.getSystemService(NetworkScoreManager.class),
newIntentFilter());
//将WifiTracker加入到WifiTracker
lifecycle.addObserver(this);
}
重载的构造函数,初始化主要的Manager
@VisibleForTesting
WifiTracker(Context context, WifiListener wifiListener,
WifiManager wifiManager, ConnectivityManager connectivityManager,
NetworkScoreManager networkScoreManager,
IntentFilter filter) {
mContext = context;
//管理wifi
mWifiManager = wifiManager;
//wifi监听者 回调上层
mListener = new WifiListenerExecutor(wifiListener);
//监控网络状态
mConnectivityManager = connectivityManager;
// check if verbose logging developer option has been turned on or off
sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0);
//广播监听
mFilter = filter;
//创建网络请求
mNetworkRequest = new NetworkRequest.Builder()
.clearCapabilities()
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
//打分机制
mNetworkScoreManager = networkScoreManager;
// TODO(sghuman): Remove this and create less hacky solution for testing
final HandlerThread workThread = new HandlerThread(TAG
+ "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
Process.THREAD_PRIORITY_BACKGROUND);
//开始工作线程
workThread.start();
setWorkThread(workThread);
}
二、WifiTracker工作分析
2.1 启动
WifiTracker观察了onStart事件,当被观察者,TVSetting中是NetworkFragment触发onStart,WifiTracker也会执行onStart()事件,进入onStart,主要就是开启wifi刷新,来更新wifi连接列表
public void onStart() {
// fetch current ScanResults instead of waiting for broadcast of fresh results
//更新wifi列表
forceUpdate();
//注册分数管理缓存
registerScoreCache();
//是否显示分数
mNetworkScoringUiEnabled =
Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.NETWORK_SCORING_UI_ENABLED, 0) == 1;
//是否显示最大速度
mMaxSpeedLabelScoreCacheAge =
Settings.Global.getLong(
mContext.getContentResolver(),
Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS);
//创建扫描服务,开始Scanning,发生Scanning消息
resumeScanning();
if (!mRegistered) {
//注册wifi广播
mContext.registerReceiver(mReceiver, mFilter, null /* permission */, mWorkHandler);
// NetworkCallback objects cannot be reused. http://b/20701525 .
mNetworkCallback = new WifiTrackerNetworkCallback();
//mConnectivityManager注册广播
mConnectivityManager.registerNetworkCallback(
mNetworkRequest, mNetworkCallback, mWorkHandler);
mRegistered = true;
}
}
2.1.1 更新wifi列表 forceUpdate
--forceUpdate调用
-----fetchScansAndConfigsAndUpdateAccessPoints
/**
* Retrieves latest scan results and wifi configs, then calls
* {@link #updateAccessPoints(List, List)}.
*/
private void fetchScansAndConfigsAndUpdateAccessPoints() {
//获得wifi扫描结果
final List<ScanResult> newScanResults = mWifiManager.getScanResults();
if (isVerboseLoggingEnabled()) {
Log.i(TAG, "Fetched scan results: " + newScanResults);
}
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
//更新AccessPoints
updateAccessPoints(newScanResults, configs);
}
---------------updateAccessPoints 更具打分机制最终生成mInternalAccessPoints,并通知UI显示
/** Update the internal list of access points. */
private void updateAccessPoints(final List<ScanResult> newScanResults,
List<WifiConfiguration> configs) {
// Map configs and scan results necessary to make AccessPoints
final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size());
if (configs != null) {
for (WifiConfiguration config : configs) {
configsByKey.put(AccessPoint.getKey(config), config);
}
}
ArrayMap<String, List<ScanResult>> scanResultsByApKey =
updateScanResultCache(newScanResults);
WifiConfiguration connectionConfig = null;
if (mLastInfo != null) {
connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId(), configs);
}
// Rather than dropping and reacquiring the lock multiple times in this method, we lock
// once for efficiency of lock acquisition time and readability
synchronized (mLock) {
// Swap the current access points into a cached list for maintaining AP listeners
List<AccessPoint> cachedAccessPoints;
cachedAccessPoints = new ArrayList<>(mInternalAccessPoints);
ArrayList<AccessPoint> accessPoints = new ArrayList<>();
final List<NetworkKey> scoresToRequest = new ArrayList<>();
for (Map.Entry<String, List<ScanResult>> entry : scanResultsByApKey.entrySet()) {
for (ScanResult result : entry.getValue()) {
NetworkKey key = NetworkKey.createFromScanResult(result);
if (key != null && !mRequestedScores.contains(key)) {
scoresToRequest.add(key);
}
}
AccessPoint accessPoint =
getCachedOrCreate(entry.getValue(), cachedAccessPoints);
if (mLastInfo != null && mLastNetworkInfo != null) {
accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
}
// Update the matching config if there is one, to populate saved network info
accessPoint.update(configsByKey.get(entry.getKey()));
accessPoints.add(accessPoint);
}
// If there were no scan results, create an AP for the currently connected network (if
// it exists).
// TODO(b/b/73076869): Add support for passpoint (ephemeral) networks
if (accessPoints.isEmpty() && connectionConfig != null) {
AccessPoint activeAp = new AccessPoint(mContext, connectionConfig);
activeAp.update(connectionConfig, mLastInfo, mLastNetworkInfo);
accessPoints.add(activeAp);
scoresToRequest.add(NetworkKey.createFromWifiInfo(mLastInfo));
}
requestScoresForNetworkKeys(scoresToRequest);
for (AccessPoint ap : accessPoints) {
ap.update(mScoreCache, mNetworkScoringUiEnabled, mMaxSpeedLabelScoreCacheAge);
}
// Pre-sort accessPoints to speed preference insertion
Collections.sort(accessPoints);
// Log accesspoints that are being removed
if (DBG()) {
Log.d(TAG, "------ Dumping SSIDs that were not seen on this scan ------");
for (AccessPoint prevAccessPoint : mInternalAccessPoints) {
if (prevAccessPoint.getSsid() == null)
continue;
String prevSsid = prevAccessPoint.getSsidStr();
boolean found = false;
for (AccessPoint newAccessPoint : accessPoints) {
if (newAccessPoint.getSsidStr() != null && newAccessPoint.getSsidStr()
.equals(prevSsid)) {
found = true;
break;
}
}
if (!found)
Log.d(TAG, "Did not find " + prevSsid + " in this scan");
}
Log.d(TAG, "---- Done dumping SSIDs that were not seen on this scan ----");
}
mInternalAccessPoints.clear();
mInternalAccessPoints.addAll(accessPoints);
}
//通知UI
conditionallyNotifyListeners();
}
2.1.2 扫描wifi resumeScanning
public void resumeScanning() {
if (mScanner == null) {
//创建扫描Handle
mScanner = new Scanner();
}
if (mWifiManager.isWifiEnabled()) {
//发生scan消息
mScanner.resume();
}
}
mScanner类负责不断的的扫描wifi
@VisibleForTesting
class Scanner extends Handler {
static final int MSG_SCAN = 0;
private int mRetry = 0;
void resume() {
if (!hasMessages(MSG_SCAN)) {
sendEmptyMessage(MSG_SCAN);
}
}
void pause() {
mRetry = 0;
removeMessages(MSG_SCAN);
}
@VisibleForTesting
boolean isScanning() {
return hasMessages(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(MSG_SCAN, WIFI_RESCAN_INTERVAL_MS);
}
}
3.总结
这一章主要分析了WifiTracker的启动,包含了扫描与更新,下一章分析WifiTracker的评分应用