前言:之前在(一百九十六)Android Q 学习WiFi的评分机制(二)梳理了CS对WiFi score变化的处理,主要是rematchAllNetworksAndRequests方法中的处理,其中有个linger相关的处理,抽取出来过一下流程。
目录
1. lingerRequest
/**
* Sets the specified request to linger on this network for the specified time. Called by
* ConnectivityService when the request is moved to another network with a higher score.
*/
public void lingerRequest(NetworkRequest request, long now, long duration) {
if (mLingerTimerForRequest.get(request.requestId) != null) {
// Cannot happen. Once a request is lingering on a particular network, we cannot
// re-linger it unless that network becomes the best for that request again, in which
// case we should have unlingered it.
Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered");
}
final long expiryMs = now + duration;
LingerTimer timer = new LingerTimer(request, expiryMs);
if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name());
mLingerTimers.add(timer);
mLingerTimerForRequest.put(request.requestId, timer);
}
NetworkAgentInfo中有上面的函数,看下具体作用
api翻译过来就是将指定的请求设置为在该网络上停留指定的时间,由ConnectivityService在request挪到更高得分的网络时调用。
看下LingerTimer是做什么的
// Networks are lingered when they become unneeded as a result of their NetworkRequests being
// satisfied by a higher-scoring network. so as to allow communication to wrap up before the
// network is taken down. This usually only happens to the default network. Lingering ends with
// either the linger timeout expiring and the network being taken down, or the network
// satisfying a request again.
public static class LingerTimer implements Comparable<LingerTimer> {
public final NetworkRequest request;
public final long expiryMs;
public LingerTimer(NetworkRequest request, long expiryMs) {
this.request = request;
this.expiryMs = expiryMs;
}
public boolean equals(Object o) {
if (!(o instanceof LingerTimer)) return false;
LingerTimer other = (LingerTimer) o;
return (request.requestId == other.request.requestId) && (expiryMs == other.expiryMs);
}
public int hashCode() {
return Objects.hash(request.requestId, expiryMs);
}
public int compareTo(LingerTimer other) {
return (expiryMs != other.expiryMs) ?
Long.compare(expiryMs, other.expiryMs) :
Integer.compare(request.requestId, other.request.requestId);
}
public String toString() {
return String.format("%s, expires %dms", request.toString(),
expiryMs - SystemClock.elapsedRealtime());
}
}
大概意思就是说如果有更好网络的时候,现有网络就有可能不再被需要了,现有网络就会被延迟关闭,延迟关闭是为了让在网络中断之前结束通信。这个通常是发生在默认网络上,总是以倒计时结束或者网络被干掉了或者这个网络又满足一个请求了的形式结束。
2. updateLingerTimer
CS
private void updateLingerState(NetworkAgentInfo nai, long now) {
// 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
// 2. If the network was lingering and there are now requests, unlinger it.
// 3. If this network is unneeded (which implies it is not lingering), and there is at least
// one lingered request, start lingering.
nai.updateLingerTimer();
if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
if (DBG) log("Unlingering " + nai.name());
nai.unlinger();
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
} else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
int lingerTime = (int) (nai.getLingerExpiry() - now);
if (DBG) log("Lingering " + nai.name() + " for " + lingerTime + "ms");
nai.linger();
logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
}
}
CS在rematch所有request后会对影响到的网络更新lingerTimer
NetworkAgentInfo
public void updateLingerTimer() {
long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
if (newExpiry == mLingerExpiryMs) return;
// Even if we're going to reschedule the timer, cancel it first. This is because the
// semantics of WakeupMessage guarantee that if cancel is called then the alarm will
// never call its callback (handleLingerComplete), even if it has already fired.
// WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
// has already been dispatched, rescheduling to some time in the future it won't stop it
// from calling its callback immediately.
if (mLingerMessage != null) {
mLingerMessage.cancel();
mLingerMessage = null;
}
if (newExpiry > 0) {
mLingerMessage = mConnService.makeWakeupMessage(
mContext, mHandler,
"NETWORK_LINGER_COMPLETE." + network.netId,
EVENT_NETWORK_LINGER_COMPLETE, this);
mLingerMessage.schedule(newExpiry);
}
mLingerExpiryMs = newExpiry;
}
这里看就开始倒计时了,倒计时时长一般为30s
3.CS的后续处理
看下CS对EVENT_NETWORK_LINGER_COMPLETE的处理
CS:
private boolean maybeHandleNetworkAgentInfoMessage(Message msg) {
switch (msg.what) {
default:
return false;
case NetworkAgentInfo.EVENT_NETWORK_LINGER_COMPLETE: {
NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
if (nai != null && isLiveNetworkAgent(nai, msg.what)) {
handleLingerComplete(nai);
}
break;
}
}
return true;
}
private void handleLingerComplete(NetworkAgentInfo oldNetwork) {
if (oldNetwork == null) {
loge("Unknown NetworkAgentInfo in handleLingerComplete");
return;
}
if (DBG) log("handleLingerComplete for " + oldNetwork.name());
// If we get here it means that the last linger timeout for this network expired. So there
// must be no other active linger timers, and we must stop lingering.
oldNetwork.clearLingerState();
if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) {
// Tear the network down.
teardownUnneededNetwork(oldNetwork);
} else {
// Put the network in the background.
updateCapabilities(oldNetwork.getCurrentScore(), oldNetwork,
oldNetwork.networkCapabilities);
}
}
倒计时结果会判断下是否是不需要了,不需要的话就干掉它
// Determines whether the network is the best (or could become the best, if it validated), for
// none of a particular type of NetworkRequests. The type of NetworkRequests considered depends
// on the value of reason:
//
// - UnneededFor.TEARDOWN: non-listen NetworkRequests. If a network is unneeded for this reason,
// then it should be torn down.
// - UnneededFor.LINGER: foreground NetworkRequests. If a network is unneeded for this reason,
// then it should be lingered.
private boolean unneeded(NetworkAgentInfo nai, UnneededFor reason) {
final int numRequests;
switch (reason) {
case TEARDOWN:
numRequests = nai.numRequestNetworkRequests();
break;
case LINGER:
numRequests = nai.numForegroundNetworkRequests();
break;
default:
Slog.wtf(TAG, "Invalid reason. Cannot happen.");
return true;
}
if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) {
return false;
}
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (reason == UnneededFor.LINGER && nri.request.isBackgroundRequest()) {
// Background requests don't affect lingering.
continue;
}
// If this Network is already the highest scoring Network for a request, or if
// there is hope for it to become one if it validated, then it is needed.
if (nri.request.isRequest() && nai.satisfies(nri.request) &&
(nai.isSatisfyingRequest(nri.request.requestId) ||
// Note that this catches two important cases:
// 1. Unvalidated cellular will not be reaped when unvalidated WiFi
// is currently satisfying the request. This is desirable when
// cellular ends up validating but WiFi does not.
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
getNetworkForRequest(nri.request.requestId).getCurrentScore() <
nai.getCurrentScoreAsValidated())) {
return false;
}
}
return true;
}
看下teardownUnneededNetwork的具体实现,直接断开与NetworkAgentInfo的连接
private void teardownUnneededNetwork(NetworkAgentInfo nai) {
if (nai.numRequestNetworkRequests() != 0) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
// Ignore listening requests.
if (nr.isListen()) continue;
loge("Dead network still had at least " + nr);
break;
}
}
nai.asyncChannel.disconnect();
}
看下asyncChannel的对端是什么
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
nai.onNetworkMonitorCreated(networkMonitor);
if (VDBG) log("Got NetworkAgent Messenger");
mNetworkAgentInfos.put(nai.messenger, nai);
synchronized (mNetworkForNetId) {
mNetworkForNetId.put(nai.network.netId, nai);
}
try {
networkMonitor.start();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
nai.asyncChannel.connect(mContext, mTrackerHandler, nai.messenger);
NetworkInfo networkInfo = nai.networkInfo;
nai.networkInfo = null;
updateNetworkInfo(nai, networkInfo);
updateUids(nai, null, nai.networkCapabilities);
}
对端是nai.messenger,看下nai.messenger是什么
是NetworkAgent
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc,
int factorySerialNumber) {
super(looper);
LOG_TAG = logTag;
mContext = context;
mFactorySerialNumber = factorySerialNumber;
if (ni == null || nc == null || lp == null) {
throw new IllegalArgumentException();
}
if (VDBG) log("Registering NetworkAgent");
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
new LinkProperties(lp), new NetworkCapabilities(nc), score, misc,
factorySerialNumber);
}
4.NetworkAgent的后续处理
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
if (DBG) log("NetworkAgent channel lost");
// let the client know CS is done with us.
unwanted();
synchronized (mPreConnectedQueue) {
mAsyncChannel = null;
}
break;
}
对端断开则调用unwanted,看下unwanted的具体实现
是个抽象方法
/**
* Called when ConnectivityService has indicated they no longer want this network.
* The parent factory should (previously) have received indication of the change
* as well, either canceling NetworkRequests or altering their score such that this
* network won't be immediately requested again.
*/
abstract protected void unwanted();
那具体看下WiFi的具体实现
private class WifiNetworkAgent extends NetworkAgent {
WifiNetworkAgent(Looper l, Context c, String tag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
super(l, c, tag, ni, nc, lp, score, misc);
}
private int mLastNetworkStatus = -1; // To detect when the status really changes
@Override
protected void unwanted() {
// Ignore if we're not the current networkAgent.
if (this != mNetworkAgent) return;
if (mVerboseLoggingEnabled) {
log("WifiNetworkAgent -> Wifi unwanted score " + Integer.toString(mWifiInfo.score));
}
unwantedNetwork(NETWORK_STATUS_UNWANTED_DISCONNECT);
}
void unwantedNetwork(int reason) {
sendMessage(CMD_UNWANTED_NETWORK, reason);
}
看下对CMD_UNWANTED_NETWORK的处理
class ConnectedState extends State {
...
@Override
public boolean processMessage(Message message) {
WifiConfiguration config = null;
boolean handleStatus = HANDLED;
switch (message.what) {
case CMD_UNWANTED_NETWORK:
if (message.arg1 == NETWORK_STATUS_UNWANTED_DISCONNECT) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
StaEvent.DISCONNECT_UNWANTED);
mWifiNative.disconnect(mInterfaceName);
transitionTo(mDisconnectingState);
} else if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN
|| message.arg1 == NETWORK_STATUS_UNWANTED_VALIDATION_FAILED) {
Log.d(TAG, (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN
? "NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN"
: "NETWORK_STATUS_UNWANTED_VALIDATION_FAILED"));
config = getCurrentWifiConfiguration();
if (config != null) {
// Disable autojoin
if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN) {
mWifiConfigManager.setNetworkValidatedInternetAccess(
config.networkId, false);
mWifiConfigManager.updateNetworkSelectionStatus(config.networkId,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_NO_INTERNET_PERMANENT);
} else {
// stop collect last-mile stats since validation fail
removeMessages(CMD_DIAGS_CONNECT_TIMEOUT);
mWifiDiagnostics.reportConnectionEvent(
WifiDiagnostics.CONNECTION_EVENT_FAILED);
mWifiConfigManager.incrementNetworkNoInternetAccessReports(
config.networkId);
// If this was not the last selected network, update network
// selection status to temporarily disable the network.
if (mWifiConfigManager.getLastSelectedNetwork() != config.networkId
&& !config.noInternetAccessExpected) {
Log.i(TAG, "Temporarily disabling network because of"
+ "no-internet access");
mWifiConfigManager.updateNetworkSelectionStatus(
config.networkId,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_NO_INTERNET_TEMPORARY);
}
}
}
}
break;
简单来说就是断开网络