Android network - NUD检测机制
- 1. 前言
- 2. 源码分析
- 2.1 ClientModeImpl
- 2.2 IpClient
- 2.3 IpReachabilityMonitor
1. 前言
在Android系统中,NUD(Neighbor Unreachable Detection)指的是网络中的邻居不可达检测机制,它用于检测设备是否能够到达特定的IP地址。当Android设备尝试与另一个设备通信时,如果发现对方不可达,它会触发NUD过程。NUD 的底层实现还是依赖kernel,Android层有服务建立通信,当kernel检测到当前网络与周边的neighbor不可达时,就会发送消息通知上层,上层处理msg
2. 源码分析
我们以 wifi 为例分析NUD检测机制,源码基于Android 14分析
2.1 ClientModeImpl
当用户点击WiFi开关,打开WiFi后,ClientModeImpl会进入 ConnectableState 状态, enter后会调用makeIpClient
// packages/modules/Wifi/service/java/com/android/server/wifi/ClientModeImpl.java
class ConnectableState extends RunnerState {
.....
@Override
public void enterImpl() {
Log.d(getTag(), "entering ConnectableState: ifaceName = " + mInterfaceName);
setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
if (mWifiGlobals.isConnectedMacRandomizationEnabled()) {
mFailedToResetMacAddress = !mWifiNative.setStaMacAddress(
mInterfaceName, MacAddressUtils.createRandomUnicastAddress());
if (mFailedToResetMacAddress) {
Log.e(getTag(), "Failed to set random MAC address on ClientMode creation");
}
}
mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));
updateCurrentConnectionInfo();
mWifiStateTracker.updateState(mInterfaceName, WifiStateTracker.INVALID);
makeIpClient();
}
......
}
private void makeIpClient() {
mIpClientCallbacks = new IpClientCallbacksImpl();
mFacade.makeIpClient(mContext, mInterfaceName, mIpClientCallbacks);
mIpClientCallbacks.awaitCreation();
}
makeIpClient主要是调用mFacade::makeIpClient(),通过FrameworkFacade创建IpClient,我们知道IpClient跟触发DHCP相关,而我们的NUD机制的注册会通过IpClient完成,看它带入的Callback实现
class IpClientCallbacksImpl extends IpClientCallbacks {
private final ConditionVariable mWaitForCreationCv = new ConditionVariable(false);
private final ConditionVariable mWaitForStopCv = new ConditionVariable(false);
@Override
public void onIpClientCreated(IIpClient ipClient) {
if (mIpClientCallbacks != this) return;
// IpClient may take a very long time (many minutes) to start at boot time. But after
// that IpClient should start pretty quickly (a few seconds).
// Blocking wait for 5 seconds first (for when the wait is short)
// If IpClient is still not ready after blocking wait, async wait (for when wait is
// long). Will drop all connection requests until IpClient is ready. Other requests
// will still be processed.
sendMessageAtFrontOfQueue(CMD_IPCLIENT_CREATED,
new IpClientManager(ipClient, getName()));
mWaitForCreationCv.open();
}
@Override
public void onPreDhcpAction() {
if (mIpClientCallbacks != this) return;
sendMessage(CMD_PRE_DHCP_ACTION);
}
@Override
public void onPostDhcpAction() {
if (mIpClientCallbacks != this) return;
sendMessage(CMD_POST_DHCP_ACTION);
}
@Override
public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
if (mIpClientCallbacks != this) return;
if (dhcpResults != null) {
sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults);
} else {
sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
}
}
@Override
public void onProvisioningSuccess(LinkProperties newLp) {
if (mIpClientCallbacks != this) return;
addPasspointInfoToLinkProperties(newLp);
mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
}
@Override
public void onProvisioningFailure(LinkProperties newLp) {
if (mIpClientCallbacks != this) return;
mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST);
sendMessage(CMD_IP_CONFIGURATION_LOST);
}
@Override
public void onLinkPropertiesChange(LinkProperties newLp) {
if (mIpClientCallbacks != this) return;
addPasspointInfoToLinkProperties(newLp);
sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
}
@Override
public void onReachabilityLost(String logMsg) {
if (mIpClientCallbacks != this) return;
mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_CMD_IP_REACHABILITY_LOST);
sendMessage(CMD_IP_REACHABILITY_LOST, logMsg);
}
@Override
public void onReachabilityFailure(ReachabilityLossInfoParcelable lossInfo) {
if (mIpClientCallbacks != this) return;
sendMessage(CMD_IP_REACHABILITY_FAILURE, lossInfo);
}
@Override
public void installPacketFilter(byte[] filter) {
if (mIpClientCallbacks != this) return;
sendMessage(CMD_INSTALL_PACKET_FILTER, filter);
}
@Override
public void startReadPacketFilter() {
if (mIpClientCallbacks != this) return;
sendMessage(CMD_READ_PACKET_FILTER);
}
@Override
public void setFallbackMulticastFilter(boolean enabled) {
if (mIpClientCallbacks != this) return;
sendMessage(CMD_SET_FALLBACK_PACKET_FILTERING, enabled);
}
@Override
public void setNeighborDiscoveryOffload(boolean enabled) {
if (mIpClientCallbacks != this) return;
sendMessage(CMD_CONFIG_ND_OFFLOAD, (enabled ? 1 : 0));
}
@Override
public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {
if (mIpClientCallbacks != this) return;
sendMessage(CMD_START_FILS_CONNECTION, 0, 0, packets);
}
@Override
public void setMaxDtimMultiplier(int multiplier) {
if (mIpClientCallbacks != this) return;
sendMessage(CMD_SET_MAX_DTIM_MULTIPLIER, multiplier);
}
@Override
public void onQuit() {
if (mIpClientCallbacks != this) return;
mWaitForStopCv.open();
}
boolean awaitCreation() {
return mWaitForCreationCv.block(IPCLIENT_STARTUP_TIMEOUT_MS);
}
boolean awaitShutdown() {
return mWaitForStopCv.block(IPCLIENT_SHUTDOWN_TIMEOUT_MS);
}
}
IpClientCallbacks包含了多个回调,其中onReachabilityLost()这个回调,是我们跟踪NUD机制的其中一个重要回调,它发送了CMD_IP_REACHABILITY_LOST msg去通知WiFi fwk进行处理(断连wifi)。
2.2 IpClient
我们跟进IpClient的构造函数来看下
// packages/modules/NetworkStack/src/android/net/ip/IpClient.java
@VisibleForTesting
public IpClient(Context context, String ifName, IIpClientCallbacks callback,
NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager,
Dependencies deps) {
super(IpClient.class.getSimpleName() + "." + ifName);
Objects.requireNonNull(ifName);
Objects.requireNonNull(callback);
mTag = getName();
mContext = context;
mInterfaceName = ifName;
mDependencies = deps;
mMetricsLog = deps.getIpConnectivityLog();
mNetworkQuirkMetrics = deps.getNetworkQuirkMetrics();
mShutdownLatch = new CountDownLatch(1);
mCm = mContext.getSystemService(ConnectivityManager.class);
mObserverRegistry = observerRegistry;
mIpMemoryStore = deps.getIpMemoryStore(context, nssManager);
sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
mLog = sSmLogs.get(mInterfaceName);
sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS));
mConnectivityPacketLog = sPktLogs.get(mInterfaceName);
mMsgStateLogger = new MessageHandlingLogger();
mCallback = new IpClientCallbacksWrapper(callback, mLog, mShim); //封装了传入进来的callback
// TODO: Consider creating, constructing, and passing in some kind of
// InterfaceController.Dependencies class.
mNetd = deps.getNetd(mContext);
mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog);
mMinRdnssLifetimeSec = mDependencies.getDeviceConfigPropertyInt(
CONFIG_MIN_RDNSS_LIFETIME, DEFAULT_MIN_RDNSS_LIFETIME);
IpClientLinkObserver.Configuration config = new IpClientLinkObserver.Configuration(
mMinRdnssLifetimeSec);
mLinkObserver = new IpClientLinkObserver(
mContext, getHandler(),
mInterfaceName,
new IpClientLinkObserver.Callback() {
@Override
public void update(boolean linkState) {
sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED, linkState
? ARG_LINKPROP_CHANGED_LINKSTATE_UP
: ARG_LINKPROP_CHANGED_LINKSTATE_DOWN);
}
@Override
public void onIpv6AddressRemoved(final Inet6Address address) {
// The update of Gratuitous NA target addresses set or unsolicited
// multicast NS source addresses set should be only accessed from the
// handler thread of IpClient StateMachine, keeping the behaviour
// consistent with relying on the non-blocking NetworkObserver callbacks,
// see {@link registerObserverForNonblockingCallback}. This can be done
// by either sending a message to StateMachine or posting a handler.
if (address.isLinkLocalAddress()) return;
getHandler().post(() -> {
mLog.log("Remove IPv6 GUA " + address
+ " from both Gratuituous NA and Multicast NS sets");
mGratuitousNaTargetAddresses.remove(address);
mMulticastNsSourceAddresses.remove(address);
});
}
@Override
public void onClatInterfaceStateUpdate(boolean add) {
// TODO: when clat interface was removed, consider sending a message to
// the IpClient main StateMachine thread, in case "NDO enabled" state
// becomes tied to more things that 464xlat operation.
getHandler().post(() -> {
mCallback.setNeighborDiscoveryOffload(add ? false : true);
});
}
},
config, mLog, mDependencies
);
mLinkProperties = new LinkProperties();
mLinkProperties.setInterfaceName(mInterfaceName);
mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
// Anything the StateMachine may access must have been instantiated
// before this point.
configureAndStartStateMachine();
// Anything that may send messages to the StateMachine must only be
// configured to do so after the StateMachine has started (above).
startStateMachineUpdaters();
}
我们可以看到IpClient会封装一次传进来的Callback参数,但只是简单的wrapper,我们最关心的onReachabilityLost()回调也是。
这里相关的初始化准备工作就完成了。NUD肯定要在用户连接了网络之后,检测才会有意义;当获取IP开始后,会进入IpClient::RunningState:
class RunningState extends State {
private ConnectivityPacketTracker mPacketTracker;
private boolean mDhcpActionInFlight;
@Override
public void enter() {
mApfFilter = maybeCreateApfFilter(mCurrentApfCapabilities);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
// rest of this IP configuration startup.
if (mApfFilter == null) {
mCallback.setFallbackMulticastFilter(mMulticastFiltering);
}
mPacketTracker = createPacketTracker();
if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
final int acceptRa =
mConfiguration.mIPv6ProvisioningMode == PROV_IPV6_LINKLOCAL ? 0 : 2;
if (isIpv6Enabled() && !startIPv6(acceptRa)) {
doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV6);
return;
}
if (isIpv4Enabled() && !isUsingPreconnection() && !startIPv4()) {
doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV4);
return;
}
final InitialConfiguration config = mConfiguration.mInitialConfig;
if ((config != null) && !applyInitialConfig(config)) {
// TODO introduce a new IpManagerEvent constant to distinguish this error case.
doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
enqueueJumpToStoppingState(DisconnectCode.DC_INVALID_PROVISIONING);
return;
}
if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
doImmediateProvisioningFailure(
IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPREACHABILITYMONITOR);
return;
}
}
......
}
其中会调用startIpReachabilityMonitor(),去创建IpReachabilityMonitor对象,NUD相关的操作都会分派给它处理
private boolean startIpReachabilityMonitor() {
try {
mIpReachabilityMonitor = mDependencies.getIpReachabilityMonitor(
mContext,
mInterfaceParams,
getHandler(),
mLog,
new IpReachabilityMonitor.Callback() {
@Override
public void notifyLost(InetAddress ip, String logMsg, NudEventType type) {
final int version = mCallback.getInterfaceVersion();
if (version >= VERSION_ADDED_REACHABILITY_FAILURE) {
final int reason = nudEventTypeToInt(type);
if (reason == INVALID_REACHABILITY_LOSS_TYPE) return;
final ReachabilityLossInfoParcelable lossInfo =
new ReachabilityLossInfoParcelable(logMsg, reason);
mCallback.onReachabilityFailure(lossInfo);
} else {
mCallback.onReachabilityLost(logMsg);
}
}
},
mConfiguration.mUsingMultinetworkPolicyTracker,
mDependencies.getIpReachabilityMonitorDeps(mContext, mInterfaceParams.name),
mNetd);
} catch (IllegalArgumentException iae) {
// Failed to start IpReachabilityMonitor. Log it and call
// onProvisioningFailure() immediately.
//
// See http://b/31038971.
logError("IpReachabilityMonitor failure: %s", iae);
mIpReachabilityMonitor = null;
}
return (mIpReachabilityMonitor != null);
}
构造IpReachabilityMonitor对象时,实现了一个IpReachabilityMonitor.Callback()回调接口,它会调用IpClient的Callback wrapper通知onReachabilityLost()事件。
NUD是为了探测周边neighbor的可达性,所以它在一次WiFi网络连接完成、拿到连接信息之后,再去开始触发探测比较正常,WiFi连接之后,ConnectModeState收到wpa_supplicant通知的连接完成事件
我们来看一次完整的wifi连接时,状态机的变化
rec[0]: time=06-20 01:45:37.298 processed=ConnectableState org=DisconnectedState dest=<null> what=CMD_IPCLIENT_CREATED screen=on 0 0
rec[1]: time=06-20 01:45:37.492 processed=ConnectableState org=DisconnectedState dest=<null> what=CMD_ENABLE_RSSI_POLL screen=on 1 0
rec[2]: time=06-20 01:45:37.544 processed=ConnectableState org=DisconnectedState dest=<null> what=CMD_SET_SUSPEND_OPT_ENABLED screen=on 0 0
rec[3]: time=06-20 01:45:37.587 processed=ConnectableState org=DisconnectedState dest=<null> what=CMD_RESET_SIM_NETWORKS screen=on 0 0
rec[4]: time=06-20 01:45:37.587 processed=ConnectableState org=DisconnectedState dest=<null> what=CMD_RESET_SIM_NETWORKS screen=on 0 0
rec[5]: time=06-20 01:46:49.169 processed=ConnectableState org=DisconnectedState dest=L2ConnectingState what=CMD_START_CONNECT screen=on 1 1010 targetConfigKey="iPhone"WPA_PSK BSSID=null targetBssid=2e:bb:5f:ef:01:91 roam=false
rec[6]: time=06-20 01:46:49.170 processed=ConnectingOrConnectedState org=L2ConnectingState dest=<null> what=SUPPLICANT_STATE_CHANGE_EVENT screen=on 0 0 ssid: bssid: 00:00:00:00:00:00 nid: -1 frequencyMhz: 0 state: INTERFACE_DISABLED
rec[7]: time=06-20 01:46:49.172 processed=ConnectingOrConnectedState org=L2ConnectingState dest=<null> what=SUPPLICANT_STATE_CHANGE_EVENT screen=on 0 0 ssid: bssid: 00:00:00:00:00:00 nid: -1 frequencyMhz: 0 state: DISCONNECTED
rec[8]: time=06-20 01:46:49.176 processed=ConnectingOrConnectedState org=L2ConnectingState dest=<null> what=SUPPLICANT_STATE_CHANGE_EVENT screen=on 0 0 ssid: "iPhone" bssid: 2e:bb:5f:ef:01:91 nid: 1 frequencyMhz: 0 state: ASSOCIATING
rec[9]: time=06-20 01:46:49.325 processed=ConnectingOrConnectedState org=L2ConnectingState dest=<null> what=SUPPLICANT_STATE_CHANGE_EVENT screen=on 0 0 ssid: "iPhone" bssid: 2e:bb:5f:ef:01:91 nid: 1 frequencyMhz: 0 state: ASSOCIATED
rec[10]: time=06-20 01:46:49.325 processed=ConnectableState org=L2ConnectingState dest=<null> what=ASSOCIATED_BSSID_EVENT screen=on 0 0 BSSID=2e:bb:5f:ef:01:91 Target Bssid=2e:bb:5f:ef:01:91 Last Bssid=2e:bb:5f:ef:01:91 roam=false
rec[11]: time=06-20 01:46:49.327 processed=ConnectingOrConnectedState org=L2ConnectingState dest=<null> what=SUPPLICANT_STATE_CHANGE_EVENT screen=on 0 0 ssid: "iPhone" bssid: 2e:bb:5f:ef:01:91 nid: 1 frequencyMhz: 0 state: FOUR_WAY_HANDSHAKE
rec[12]: time=06-20 01:46:49.353 processed=ConnectingOrConnectedState org=L2ConnectingState dest=<null> what=SUPPLICANT_STATE_CHANGE_EVENT screen=on 0 0 ssid: "iPhone" bssid: 2e:bb:5f:ef:01:91 nid: 1 frequencyMhz: 0 state: GROUP_HANDSHAKE
rec[13]: time=06-20 01:46:49.367 processed=ConnectingOrConnectedState org=L2ConnectingState dest=L3ProvisioningState what=NETWORK_CONNECTION_EVENT screen=on 1 false 2e:bb:5f:ef:01:91 nid=1 "iPhone"WPA_PSK last=
rec[14]: time=06-20 01:46:49.381 processed=ConnectingOrConnectedState org=L3ProvisioningState dest=<null> what=SUPPLICANT_STATE_CHANGE_EVENT screen=on 0 0 ssid: "iPhone" bssid: 2e:bb:5f:ef:01:91 nid: 1 frequencyMhz: 0 state: COMPLETED
rec[15]: time=06-20 01:46:49.398 processed=ConnectableState org=L3ProvisioningState dest=<null> what=CMD_CONFIG_ND_OFFLOAD screen=on 1 0
rec[16]: time=06-20 01:46:49.399 processed=ConnectableState org=L3ProvisioningState dest=<null> what=CMD_SET_FALLBACK_PACKET_FILTERING screen=on enabled=true
rec[17]: time=06-20 01:46:49.404 processed=ConnectableState org=L3ProvisioningState dest=<null> what=CMD_UPDATE_LINKPROPERTIES screen=on 0 0
rec[18]: time=06-20 01:46:49.405 processed=ConnectableState org=L3ProvisioningState dest=<null> what=CMD_SET_MAX_DTIM_MULTIPLIER screen=on maximum multiplier=1
rec[19]: time=06-20 01:46:49.528 processed=L2ConnectedState org=L3ProvisioningState dest=<null> what=CMD_PRE_DHCP_ACTION screen=on 0 0 txpkts=10,0,0
rec[20]: time=06-20 01:46:49.530 processed=L2ConnectedState org=L3ProvisioningState dest=<null> what=CMD_PRE_DHCP_ACTION_COMPLETE screen=on uid=1000 0 0
rec[21]: time=06-20 01:46:49.646 processed=L2ConnectedState org=L3ProvisioningState dest=<null> what=CMD_POST_DHCP_ACTION screen=on
rec[22]: time=06-20 01:46:49.647 processed=L2ConnectedState org=L3ProvisioningState dest=<null> what=CMD_IPV4_PROVISIONING_SUCCESS screen=on com.android.wifi.x.android.net.DhcpResultsParcelable{baseConfiguration: IP address 172.20.10.2/28 Gateway 172.20.10.1 DNS servers: [ 172.20.10.1 ] Domains , leaseDuration: 85536, mtu: 0, serverAddress: 172.20.10.1, vendorInfo: ANDROID_METERED, serverHostName: iPhone, captivePortalApiUrl: null}
rec[23]: time=06-20 01:46:49.647 processed=ConnectableState org=L3ProvisioningState dest=<null> what=CMD_UPDATE_LINKPROPERTIES screen=on 0 0 v4r
rec[24]: time=06-20 01:46:49.647 processed=ConnectableState org=L3ProvisioningState dest=<null> what=CMD_UPDATE_LINKPROPERTIES screen=on 0 0 v4 v4r v4dns
rec[25]: time=06-20 01:46:49.648 processed=L2ConnectedState org=L3ProvisioningState dest=L3ConnectedState what=CMD_IP_CONFIGURATION_SUCCESSFUL screen=on 0 0
rec[26]: time=06-20 01:46:49.653 processed=L2ConnectedState org=L3ConnectedState dest=<null> what=CMD_ONESHOT_RSSI_POLL screen=on 0 0 "iPhone" 2e:bb:5f:ef:01:91 rssi=-70 f=2437 sc=null link=65 tx=0.3, 0.0, 0.0 rx=0.0 bcn=0 [on:0 tx:0 rx:0 period:832291253] from screen [on:0 period:832291253] score=0
rec[27]: time=06-20 01:46:50.256 processed=L3ConnectedState org=L3ConnectedState dest=<null> what=CMD_NETWORK_STATUS screen=on 1 0
rec[28]: time=06-20 01:46:51.024 processed=ConnectableState org=L3ConnectedState dest=<null> what=CMD_UPDATE_LINKPROPERTIES screen=on 0 0 v4 v4r v4dns
rec[29]: time=07-04 16:09:02.894 processed=ConnectableState org=L3ConnectedState dest=<null> what=CMD_UPDATE_LINKPROPERTIES screen=on 0 0 v4 v4r v4dns v6r v6dns
rec[30]: time=07-04 16:09:03.869 processed=ConnectableState org=L3ConnectedState dest=<null> what=CMD_UPDATE_LINKPROPERTIES screen=on 0 0 v4 v4r v4dns v6 v6r v6dns
rec[31]: time=07-04 16:09:03.871 processed=ConnectableState org=L3ConnectedState dest=<null> what=CMD_SET_MAX_DTIM_MULTIPLIER screen=on maximum multiplier=2
rec[32]: time=07-04 16:09:04.574 processed=ConnectableState org=L3ConnectedState dest=<null> what=CMD_UPDATE_LINKPROPERTIES screen=on 0 0 v4 v4r v4dns v6 v6r v6dns
rec[33]: time=07-04 16:09:11.381 processed=L3ConnectedState org=L3ConnectedState dest=<null> what=what:131383 screen=on
rec[34]: time=07-04 16:09:11.792 processed=L2ConnectedState org=L3ConnectedState dest=<null> what=CMD_ONESHOT_RSSI_POLL screen=on 0 0 "iPhone" 2e:bb:5f:ef:01:91 rssi=-63 f=2437 sc=null link=57 tx=7.9, 0.0, 0.0 rx=4.5 bcn=0 [on:0 tx:0 rx:0 period:1261342139] from screen [on:0 period:2093633392] score=0
rec[35]: time=07-04 16:09:29.250 processed=L2ConnectedState org=L3ConnectedState dest=<null> what=CMD_ONESHOT_RSSI_POLL screen=on 0 0 "iPhone" 2e:bb:5f:ef:01:91 rssi=-66 f=2437 sc=null link=26 tx=2.8, 0.0, 0.0 rx=1.7 bcn=0 [on:0 tx:0 rx:0 period:17458] from screen [on:0 period:2093650850] score=0
根据log和代码,我们发现在DHCP之后,wifi状态机进到L3ConnectedState时,会收到CMD_ONESHOT_RSSI_POLL 消息
// packages/modules/Wifi/service/java/com/android/server/wifi/ClientModeImpl.java
class L3ConnectedState extends RunnerState {
L3ConnectedState(int threshold) {
super(threshold, mWifiInjector.getWifiHandlerLocalLog());
}
@Override
public void enterImpl() {
if (mVerboseLoggingEnabled) {
log("Enter ConnectedState mScreenOn=" + mScreenOn);
}
.......
updateLinkLayerStatsRssiAndScoreReport(); // 发送CMD_ONESHOT_RSSI_POLL
.......
}
......
}
private void updateLinkLayerStatsRssiAndScoreReport() {
sendMessage(CMD_ONESHOT_RSSI_POLL);
}
CMD_ONESHOT_RSSI_POLL被接受到后会调用IpClient::confirmConfiguration()确认网络配置
case CMD_ONESHOT_RSSI_POLL: {
if (!mEnableRssiPolling) {
updateLinkLayerStatsRssiDataStallScoreReport();
}
break;
}
/**
* Fetches link stats, updates Wifi Data Stall, Score Card and Score Report.
*/
private WifiLinkLayerStats updateLinkLayerStatsRssiDataStallScoreReport() {
......
if (mWifiScoreReport.shouldCheckIpLayer()) {
if (mIpClient != null) {
mIpClient.confirmConfiguration();
}
mWifiScoreReport.noteIpCheck();
}
mLastLinkLayerStats = stats;
return stats;
}
}
然后执行probe链路上的neighbours网络
// packages/modules/NetworkStack/src/android/net/ip/IpClient.java
/**
* Confirm the provisioning configuration.
*/
public void confirmConfiguration() {
sendMessage(CMD_CONFIRM);
}
......
class RunningState extends State {
......
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_STOP:
transitionTo(mStoppingState);
break;
case CMD_START:
logError("ALERT: START received in StartedState. Please fix caller.");
break;
case CMD_CONFIRM:
// TODO: Possibly introduce a second type of confirmation
// that both probes (a) on-link neighbors and (b) does
// a DHCPv4 RENEW. We used to do this on Wi-Fi framework
// roams.
if (mIpReachabilityMonitor != null) {
mIpReachabilityMonitor.probeAll();
}
break;
......
}
}
这里会发现所有的操作都会由IpReachabilityMonitor处理
2.3 IpReachabilityMonitor
我们再回头看IpReachabilityMonitor的构造实现
// packages/modules/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
@VisibleForTesting
public IpReachabilityMonitor(Context context, InterfaceParams ifParams, Handler h,
SharedLog log, Callback callback, boolean usingMultinetworkPolicyTracker,
Dependencies dependencies, final IpConnectivityLog metricsLog, final INetd netd) {
if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
mContext = context;
mInterfaceParams = ifParams;
mLog = log.forSubComponent(TAG);
mCallback = callback;
mUsingMultinetworkPolicyTracker = usingMultinetworkPolicyTracker;
mCm = context.getSystemService(ConnectivityManager.class);
mDependencies = dependencies;
mMulticastResolicitEnabled = dependencies.isFeatureEnabled(context,
IP_REACHABILITY_MCAST_RESOLICIT_VERSION, true /* defaultEnabled */);
mIgnoreIncompleteIpv6DnsServerEnabled = dependencies.isFeatureEnabled(context,
IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION,
false /* defaultEnabled */);
mIgnoreIncompleteIpv6DefaultRouterEnabled = dependencies.isFeatureEnabled(context,
IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DEFAULT_ROUTER_VERSION,
false /* defaultEnabled */);
mMetricsLog = metricsLog;
mNetd = netd;
Preconditions.checkNotNull(mNetd);
Preconditions.checkArgument(!TextUtils.isEmpty(mInterfaceParams.name));
// In case the overylaid parameters specify an invalid configuration, set the parameters
// to the hardcoded defaults first, then set them to the values used in the steady state.
try {
int numResolicits = mMulticastResolicitEnabled
? NUD_MCAST_RESOLICIT_NUM
: INVALID_NUD_MCAST_RESOLICIT_NUM;
setNeighborParameters(MIN_NUD_SOLICIT_NUM, MIN_NUD_SOLICIT_INTERVAL_MS, numResolicits);
} catch (Exception e) {
Log.e(TAG, "Failed to adjust neighbor parameters with hardcoded defaults");
}
setNeighbourParametersForSteadyState();
mIpNeighborMonitor = dependencies.makeIpNeighborMonitor(h, mLog,
(NeighborEvent event) -> {
if (mInterfaceParams.index != event.ifindex) return;
if (!mNeighborWatchList.containsKey(event.ip)) return;
final NeighborEvent prev = mNeighborWatchList.put(event.ip, event);
// TODO: Consider what to do with other states that are not within
// NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE).
if (event.nudState == StructNdMsg.NUD_FAILED) {
// After both unicast probe and multicast probe(if mcast_resolicit is not 0)
// attempts fail, trigger the neighbor lost event and disconnect.
mLog.w("ALERT neighbor went from: " + prev + " to: " + event);
handleNeighborLost(prev, event);
} else if (event.nudState == StructNdMsg.NUD_REACHABLE) {
handleNeighborReachable(prev, event);
}
});
mIpNeighborMonitor.start();
mIpReachabilityMetrics = dependencies.getIpReachabilityMonitorMetrics();
}
mCallback保存了我们传入的Callback对象,它实现了notifyLost()函数;IpNeighborMonitor会接受、解析来自kernel的packet,包含了我们需要monitor哪些IP,以及接收NUD lost的结果,并调用handleNeighborLost()进行接下去通知WiFi framework NUD lost结果的处理。
IpNeighborMonitor接收来自IpReachabilityMonitor的处理,创建IpNeighborMonitor的时候,传入了一个用lambda表达式创建的NeighborEventConsumer对象,它实现了accept函数,主要处理:
1、解析从kernel上报的需要监听的IP地址集,它保存在mNeighborWatchList集合中
2、判断当前的event是不是通知NUD_FAILED,如果是就调用handleNeighborLost()处理:
// frameworks/libs/net/common/device/com/android/net/module/util/ip/IpNeighborMonitor.java
public class IpNeighborMonitor extends NetlinkMonitor {
private static final String TAG = IpNeighborMonitor.class.getSimpleName();
private static final boolean DBG = false;
private static final boolean VDBG = false;
/**
* Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
* for the given IP address on the specified interface index.
*
* @return 0 if the request was successfully passed to the kernel; otherwise return
* a non-zero error code.
*/
public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
if (DBG) Log.d(TAG, msgSnippet);
// 创建一个Netlink消息,用于请求内核执行邻居探测。
final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
try {
// 使用NetlinkUtils发送一个单次内核消息。
NetlinkUtils.sendOneShotKernelMessage(NETLINK_ROUTE, msg);
} catch (ErrnoException e) {
Log.e(TAG, "Error " + msgSnippet + ": " + e);
return -e.errno;
}
return 0;
}
/**
* An event about a neighbor.
*/
public static class NeighborEvent {
public final long elapsedMs;
public final short msgType;
public final int ifindex;
@NonNull
public final InetAddress ip;
public final short nudState;
@NonNull
public final MacAddress macAddr;
public NeighborEvent(long elapsedMs, short msgType, int ifindex, @NonNull InetAddress ip,
short nudState, @NonNull MacAddress macAddr) {
this.elapsedMs = elapsedMs;
this.msgType = msgType;
this.ifindex = ifindex;
this.ip = ip;
this.nudState = nudState;
this.macAddr = macAddr;
}
boolean isConnected() {
return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
}
public boolean isValid() {
return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
}
@Override
public String toString() {
final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
return j.add("@" + elapsedMs)
.add(stringForNlMsgType(msgType, NETLINK_ROUTE))
.add("if=" + ifindex)
.add(ip.getHostAddress())
.add(StructNdMsg.stringForNudState(nudState))
.add("[" + macAddr + "]")
.toString();
}
}
/**
* A client that consumes NeighborEvent instances.
* Implement this to listen to neighbor events.
*/
public interface NeighborEventConsumer {
// 每个在netlink套接字上接收到的邻居事件都会传递到这里。
// 子类应过滤感兴趣的事件。
/**
* Consume a neighbor event
* @param event the event
*/
void accept(NeighborEvent event);
}
private final NeighborEventConsumer mConsumer;
public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
super(h, log, TAG, NETLINK_ROUTE, OsConstants.RTMGRP_NEIGH);
mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
}
@Override
public void processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs) {
if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
mLog.e("non-rtnetlink neighbor msg: " + nlMsg);
return;
}
final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) nlMsg;
final short msgType = neighMsg.getHeader().nlmsg_type;
final StructNdMsg ndMsg = neighMsg.getNdHeader();
if (ndMsg == null) {
mLog.e("RtNetlinkNeighborMessage without ND message header!");
return;
}
final int ifindex = ndMsg.ndm_ifindex;
final InetAddress destination = neighMsg.getDestination();
final short nudState =
(msgType == RTM_DELNEIGH)
? StructNdMsg.NUD_NONE
: ndMsg.ndm_state; // 获取邻居状态。
final NeighborEvent event = new NeighborEvent(
whenMs, msgType, ifindex, destination, nudState,
getMacAddress(neighMsg.getLinkLayerAddress()));
if (VDBG) {
Log.d(TAG, neighMsg.toString());
}
if (DBG) {
Log.d(TAG, event.toString());
}
mConsumer.accept(event); // 将邻居事件传递给IpReachabilityMonitor进行处理。
}
private static MacAddress getMacAddress(byte[] linkLayerAddress) {
if (linkLayerAddress != null) {
try {
return MacAddress.fromBytes(linkLayerAddress);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
}
}
return null;
}
}
IpNeighborMonitor的代码中startKernelNeighborProbe这个方法用于请求内核执行邻居可达性探测(IPv4 ARP或IPv6 ND)对于给定的IP地址和指定的接口索引。processNetlinkMessage方法是NetlinkMonitor类的一个覆盖方法,用于处理Netlink消息。
至于具体解析packet的过程这里仔细叙述了,简单分析下IpNeighborMonitor的几个父类的代码即可,下面是他的父类关系图。
// frameworks/libs/net/common/device/com/android/net/module/util/ip/NetlinkMonitor.java
public class NetlinkMonitor extends PacketReader {
......
public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag,
int family, int bindGroups, int sockRcvbufSize) {
super(h, NetlinkUtils.DEFAULT_RECV_BUFSIZE);
mLog = log.forSubComponent(tag);
mTag = tag;
mFamily = family;
mBindGroups = bindGroups;
mSockRcvbufSize = sockRcvbufSize;
}
public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag,
int family, int bindGroups) {
this(h, log, tag, family, bindGroups, DEFAULT_SOCKET_RECV_BUFSIZE);
}
@Override
protected FileDescriptor createFd() {
FileDescriptor fd = null;
try {
fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, mFamily);
if (mSockRcvbufSize != DEFAULT_SOCKET_RECV_BUFSIZE) {
try {
Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, mSockRcvbufSize);
} catch (ErrnoException e) {
Log.wtf(mTag, "Failed to set SO_RCVBUF to " + mSockRcvbufSize, e);
}
}
Os.bind(fd, makeNetlinkSocketAddress(0, mBindGroups));
NetlinkUtils.connectSocketToNetlink(fd);
if (DBG) {
final SocketAddress nlAddr = Os.getsockname(fd);
Log.d(mTag, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
}
} catch (ErrnoException | SocketException e) {
logError("Failed to create rtnetlink socket", e);
closeSocketQuietly(fd);
return null;
}
return fd;
}
@Override
protected void handlePacket(byte[] recvbuf, int length) {
final long whenMs = SystemClock.elapsedRealtime();
final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
byteBuffer.order(ByteOrder.nativeOrder());
while (byteBuffer.remaining() > 0) {
try {
final int position = byteBuffer.position();
final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer, mFamily);
if (nlMsg == null || nlMsg.getHeader() == null) {
byteBuffer.position(position);
mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
break;
}
if (nlMsg instanceof NetlinkErrorMessage) {
mLog.e("netlink error: " + nlMsg);
continue;
}
processNetlinkMessage(nlMsg, whenMs);
} catch (Exception e) {
mLog.e("Error handling netlink message", e);
}
}
}
......
protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) { }
}
// frameworks/libs/net/common/device/com/android/net/module/util/PacketReader.java
public abstract class PacketReader extends FdEventsReader<byte[]> {
public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
protected PacketReader(Handler h) {
this(h, DEFAULT_RECV_BUF_SIZE);
}
protected PacketReader(Handler h, int recvBufSize) {
super(h, new byte[max(recvBufSize, DEFAULT_RECV_BUF_SIZE)]);
}
@Override
protected final int recvBufSize(byte[] buffer) {
return buffer.length;
}
/**
* Subclasses MAY override this to change the default read() implementation
* in favour of, say, recvfrom().
*
* Implementations MUST return the bytes read or throw an Exception.
*/
@Override
protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception {
return Os.read(fd, packetBuffer, 0, packetBuffer.length);
}
}
public abstract class FdEventsReader<BufferType> {
private static final String TAG = FdEventsReader.class.getSimpleName();
private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
private static final int UNREGISTER_THIS_FD = 0;
@NonNull
private final Handler mHandler;
@NonNull
private final MessageQueue mQueue;
@NonNull
private final BufferType mBuffer;
@Nullable
private FileDescriptor mFd;
private long mPacketsReceived;
protected static void closeFd(FileDescriptor fd) {
try {
SocketUtils.closeSocket(fd);
} catch (IOException ignored) {
}
}
protected FdEventsReader(@NonNull Handler h, @NonNull BufferType buffer) {
mHandler = h;
mQueue = mHandler.getLooper().getQueue();
mBuffer = buffer;
}
@VisibleForTesting
@NonNull
protected MessageQueue getMessageQueue() {
return mQueue;
}
/** Start this FdEventsReader. */
public boolean start() {
if (!onCorrectThread()) {
throw new IllegalStateException("start() called from off-thread");
}
return createAndRegisterFd();
}
/** Stop this FdEventsReader and destroy the file descriptor. */
public void stop() {
if (!onCorrectThread()) {
throw new IllegalStateException("stop() called from off-thread");
}
unregisterAndDestroyFd();
}
@NonNull
public Handler getHandler() {
return mHandler;
}
protected abstract int recvBufSize(@NonNull BufferType buffer);
/** Returns the size of the receive buffer. */
public int recvBufSize() {
return recvBufSize(mBuffer);
}
public final long numPacketsReceived() {
return mPacketsReceived;
}
@Nullable
protected abstract FileDescriptor createFd();
protected abstract int readPacket(@NonNull FileDescriptor fd, @NonNull BufferType buffer)
throws Exception;
protected void handlePacket(@NonNull BufferType recvbuf, int length) {}
protected boolean handleReadError(@NonNull ErrnoException e) {
logError("readPacket error: ", e);
return true; // by default, stop reading on any error.
}
protected void logError(@NonNull String msg, @Nullable Exception e) {}
protected void onStart() {}
protected void onStop() {}
private boolean createAndRegisterFd() {
if (mFd != null) return true;
try {
mFd = createFd();
} catch (Exception e) {
logError("Failed to create socket: ", e);
closeFd(mFd);
mFd = null;
}
if (mFd == null) return false;
getMessageQueue().addOnFileDescriptorEventListener(
mFd,
FD_EVENTS,
(fd, events) -> {
if (!isRunning() || !handleInput()) {
unregisterAndDestroyFd();
return UNREGISTER_THIS_FD;
}
return FD_EVENTS;
});
onStart();
return true;
}
protected boolean isRunning() {
return (mFd != null) && mFd.valid();
}
// Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error.
private boolean handleInput() {
while (isRunning()) {
final int bytesRead;
try {
bytesRead = readPacket(mFd, mBuffer);
if (bytesRead < 1) {
if (isRunning()) logError("Socket closed, exiting", null);
break;
}
mPacketsReceived++;
} catch (ErrnoException e) {
if (e.errno == OsConstants.EAGAIN) {
// We've read everything there is to read this time around.
return true;
} else if (e.errno == OsConstants.EINTR) {
continue;
} else {
if (!isRunning()) break;
final boolean shouldStop = handleReadError(e);
if (shouldStop) break;
continue;
}
} catch (Exception e) {
if (isRunning()) logError("readPacket error: ", e);
break;
}
try {
handlePacket(mBuffer, bytesRead);
} catch (Exception e) {
logError("handlePacket error: ", e);
Log.wtf(TAG, "Error handling packet", e);
}
}
return false;
}
private void unregisterAndDestroyFd() {
if (mFd == null) return;
getMessageQueue().removeOnFileDescriptorEventListener(mFd);
closeFd(mFd);
mFd = null;
onStop();
}
private boolean onCorrectThread() {
return (mHandler.getLooper() == Looper.myLooper());
}
}
从关系上来看IpNeighborMonitor.()start会创建出一个获取kernel netlink消息的socket,并持续获取kernel的消息。
总的来说,FdEventsReader 提供了一个基础的事件读取框架,PacketReader 扩展了这个框架以读取数据包,NetlinkMonitor 进一步扩展了这个框架以处理 Netlink 消息,而 IpNeighborMonitor 专门处理 IP 邻居消息。每个类都在前一个类的基础上添加了更具体的逻辑和功能。
这时候基本框架流程已经梳理清楚了,但本着将流程跟完的原则,再看下前面还未分析的IpReachabilityMonitor::probeAll()调用,以及wifi framework的处理
// packages/modules/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
public void probeAll(boolean dueToRoam) {
setNeighbourParametersPostRoaming();
final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet());
if (!ipProbeList.isEmpty()) {
mDependencies.acquireWakeLock(getProbeWakeLockDuration());
}
for (InetAddress ip : ipProbeList) {
final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceParams.index, ip);
mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
ip.getHostAddress(), rval));
logEvent(IpReachabilityEvent.PROBE, rval);
}
mLastProbeTimeMs = SystemClock.elapsedRealtime();
if (dueToRoam) {
mLastProbeDueToRoamMs = mLastProbeTimeMs;
} else {
mLastProbeDueToConfirmMs = mLastProbeTimeMs;
}
}
probeAll()中会遍历mNeighborWatchList的IP地址,分别对其进行NUD检测
通过Netlink机制请求kernel进行probe后,Framework能做的就是等待结果了;如果kernel检测遇到了NUD失败,这个信息经过packet解析、封装成event之后,会由IpNeighborMonitor::NeighborEventConsumer mConsumer处理,mConsumer也就是IpReachabilityMonitor创建IpNeighborMonitor时,用lambda表达式创建的对象
mIpNeighborMonitor = dependencies.makeIpNeighborMonitor(h, mLog,
(NeighborEvent event) -> {
if (mInterfaceParams.index != event.ifindex) return;
if (!mNeighborWatchList.containsKey(event.ip)) return;
final NeighborEvent prev = mNeighborWatchList.put(event.ip, event);
// TODO: Consider what to do with other states that are not within
// NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE).
if (event.nudState == StructNdMsg.NUD_FAILED) {
// After both unicast probe and multicast probe(if mcast_resolicit is not 0)
// attempts fail, trigger the neighbor lost event and disconnect.
mLog.w("ALERT neighbor went from: " + prev + " to: " + event);
handleNeighborLost(prev, event);
} else if (event.nudState == StructNdMsg.NUD_REACHABLE) {
handleNeighborReachable(prev, event);
}
});
如果NeighborEvent的msg是NUD_FAILED,说明NUD检测失败,需要通知给上层这个事件
private void handleNeighborLost(@Nullable final NeighborEvent prev,
@NonNull final NeighborEvent event) {
final LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
......
final boolean lostProvisioning =
(mLinkProperties.isIpv4Provisioned() && !whatIfLp.isIpv4Provisioned())
|| (mLinkProperties.isIpv6Provisioned() && !whatIfLp.isIpv6Provisioned()
&& !ignoreIncompleteIpv6Neighbor);
final NudEventType type = getNudFailureEventType(isFromProbe(),
isNudFailureDueToRoam(), lostProvisioning);
if (lostProvisioning) {
final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
Log.w(TAG, logMsg);
// TODO: remove |ip| when the callback signature no longer has
// an InetAddress argument.
mCallback.notifyLost(ip, logMsg, type);
}
logNudFailed(event, type);
}
随之会通过比较当前两个IP的配置信息来判断,连接是否已经不可达了,如果是就会通过mCallback对象回调notifyLost()通知上层,由前面可知这个Callback对象经过了几次封装,为了节省时间我们直接看ClientModeImpl中最原始的那个Callback实现
class IpClientCallbacksImpl extends IpClientCallbacks {
......
@Override
public void onReachabilityLost(String logMsg) {
if (mIpClientCallbacks != this) return;
mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_CMD_IP_REACHABILITY_LOST);
sendMessage(CMD_IP_REACHABILITY_LOST, logMsg);
}
@Override
public void onReachabilityFailure(ReachabilityLossInfoParcelable lossInfo) {
if (mIpClientCallbacks != this) return;
sendMessage(CMD_IP_REACHABILITY_FAILURE, lossInfo);
}
......
}
IpClientCallbacksImpl::onReachabilityLost()会被调用,并发送CMD_IP_REACHABILITY_LOST msg,看该msg的处理过程
class L2ConnectedState extends RunnerState {
@Override
public boolean processMessageImpl(Message message) {
......
case CMD_IP_REACHABILITY_LOST: {
if (mVerboseLoggingEnabled && message.obj != null) log((String) message.obj);
mWifiDiagnostics.triggerBugReportDataCapture(
WifiDiagnostics.REPORT_REASON_REACHABILITY_LOST);
mWifiMetrics.logWifiIsUnusableEvent(mInterfaceName,
WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST);
mWifiMetrics.addToWifiUsabilityStatsList(mInterfaceName,
WifiUsabilityStats.LABEL_BAD,
WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1);
if (mWifiGlobals.getIpReachabilityDisconnectEnabled()) {
handleIpReachabilityLost();
} else {
logd("CMD_IP_REACHABILITY_LOST but disconnect disabled -- ignore");
}
break;
}
......
}
......
}
private void handleIpReachabilityLost() {
mWifiBlocklistMonitor.handleBssidConnectionFailure(mWifiInfo.getBSSID(),
getConnectedWifiConfiguration(),
WifiBlocklistMonitor.REASON_ABNORMAL_DISCONNECT, mWifiInfo.getRssi());
mWifiScoreCard.noteIpReachabilityLost(mWifiInfo);
mWifiInfo.setInetAddress(null);
mWifiInfo.setMeteredHint(false);
// Disconnect via supplicant, and let autojoin retry connecting to the network.
mWifiNative.disconnect(mInterfaceName);
updateCurrentConnectionInfo();
}
L2ConnectedState会处理CMD_IP_REACHABILITY_LOST msg,如果mIpReachabilityDisconnectEnabled配置为true,就会去主动disconnect WiFi,如果不想断连wifi 就将其配置为false即可