Send normal termination metrics on wifi off
When turning wifi off, the interface gets torn down and empty
LinkProperties are received before wifi calls stop(). This causes a loss
of provisioning to be logged, instead of normal termination.
Watch interface link status up/down events, and when provisioning is
lost when the interface is down, consider it a normal termination.
Bug: 151796056
Test: manual: turn wifi off, observe events
Test: atest NetworkStackIntegrationTests (see also test-only change)
Original-Change: https://android-review.googlesource.com/1343236
Merged-In: I9d086a199de0017aa425219d20882211423925e0
Change-Id: I9d086a199de0017aa425219d20882211423925e0
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index 1591f43..c4f46ae 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -389,6 +389,9 @@
private static final int CMD_COMPLETE_PRECONNECTION = 16;
private static final int CMD_UPDATE_L2INFORMATION = 17;
+ private static final int ARG_LINKPROP_CHANGED_LINKSTATE_DOWN = 0;
+ private static final int ARG_LINKPROP_CHANGED_LINKSTATE_UP = 1;
+
// Internal commands to use instead of trying to call transitionTo() inside
// a given State's enter() method. Calling transitionTo() from enter/exit
// encounters a Log.wtf() that can cause trouble on eng builds.
@@ -596,7 +599,9 @@
mLinkObserver = new IpClientLinkObserver(
mContext, getHandler(),
mInterfaceName,
- () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED),
+ (ifaceUp) -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED, ifaceUp
+ ? ARG_LINKPROP_CHANGED_LINKSTATE_UP
+ : ARG_LINKPROP_CHANGED_LINKSTATE_DOWN),
config, mLog) {
@Override
public void onInterfaceAdded(String iface) {
@@ -2053,12 +2058,14 @@
case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
// EVENT_NETLINK_LINKPROPERTIES_CHANGED message will be received in both of
- // provisioning loss and normal user termination case (e.g. turn off wifi or
- // switch to another wifi ssid), hence, checking current interface change
- // status (down or up) would help distinguish.
- final boolean ifUp = (msg.arg1 != 0);
+ // provisioning loss and normal user termination cases (e.g. turn off wifi or
+ // switch to another wifi ssid), hence, checking the current interface link
+ // state (down or up) helps distinguish the two cases: if the link state is
+ // down, provisioning is only lost because the link is being torn down (for
+ // example when turning off wifi), so treat it as a normal termination.
if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
- transitionToStoppingState(ifUp ? DisconnectCode.DC_PROVISIONING_FAIL
+ final boolean linkStateUp = (msg.arg1 == ARG_LINKPROP_CHANGED_LINKSTATE_UP);
+ transitionToStoppingState(linkStateUp ? DisconnectCode.DC_PROVISIONING_FAIL
: DisconnectCode.DC_NORMAL_TERMINATION);
}
break;
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java
index dcbca94..46b3844 100644
--- a/src/android/net/ip/IpClientLinkObserver.java
+++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -89,8 +89,13 @@
public interface Callback {
/**
* Called when some properties of the link were updated.
+ *
+ * @param linkState Whether the interface link state is up as per the latest
+ * {@link #onInterfaceLinkStateChanged(String, boolean)} callback. This
+ * should only be used for metrics purposes, as it could be inconsistent
+ * with {@link #getLinkProperties()} in particular.
*/
- void update();
+ void update(boolean linkState);
}
/** Configuration parameters for IpClientLinkObserver. */
@@ -105,6 +110,7 @@
private final String mInterfaceName;
private final Callback mCallback;
private final LinkProperties mLinkProperties;
+ private boolean mInterfaceLinkState;
private DnsServerRepository mDnsServerRepository;
private final AlarmManager mAlarmManager;
private final Configuration mConfig;
@@ -121,6 +127,7 @@
mLinkProperties = new LinkProperties();
mLinkProperties.setInterfaceName(mInterfaceName);
mConfig = config;
+ mInterfaceLinkState = true; // Assume up by default
mDnsServerRepository = new DnsServerRepository(config.minRdnssLifetime);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mNetlinkMonitor = new MyNetlinkMonitor(h, log, mTag);
@@ -149,7 +156,15 @@
// interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd
// code that parses them will not be able to resolve the ifindex to an interface name.
clearLinkProperties();
- mCallback.update();
+ mCallback.update(getInterfaceLinkState());
+ }
+ }
+
+ @Override
+ public void onInterfaceLinkStateChanged(String iface, boolean state) {
+ if (mInterfaceName.equals(iface)) {
+ maybeLog("interfaceLinkStateChanged", iface + (state ? " up" : " down"));
+ setInterfaceLinkState(state);
}
}
@@ -162,7 +177,7 @@
changed = mLinkProperties.addLinkAddress(address);
}
if (changed) {
- mCallback.update();
+ mCallback.update(getInterfaceLinkState());
}
}
}
@@ -176,7 +191,7 @@
changed = mLinkProperties.removeLinkAddress(address);
}
if (changed) {
- mCallback.update();
+ mCallback.update(getInterfaceLinkState());
}
}
}
@@ -190,7 +205,7 @@
changed = mLinkProperties.addRoute(route);
}
if (changed) {
- mCallback.update();
+ mCallback.update(getInterfaceLinkState());
}
}
}
@@ -204,7 +219,7 @@
changed = mLinkProperties.removeRoute(route);
}
if (changed) {
- mCallback.update();
+ mCallback.update(getInterfaceLinkState());
}
}
}
@@ -218,7 +233,7 @@
synchronized (this) {
mDnsServerRepository.setDnsServersOn(mLinkProperties);
}
- mCallback.update();
+ mCallback.update(getInterfaceLinkState());
}
}
}
@@ -243,6 +258,14 @@
mLinkProperties.setInterfaceName(mInterfaceName);
}
+ private synchronized boolean getInterfaceLinkState() {
+ return mInterfaceLinkState;
+ }
+
+ private synchronized void setInterfaceLinkState(boolean state) {
+ mInterfaceLinkState = state;
+ }
+
/** Notifies this object of new interface parameters. */
public void setInterfaceParams(InterfaceParams params) {
mNetlinkMonitor.setIfindex(params.index);
@@ -355,7 +378,7 @@
cancelPref64Alarm();
}
- mCallback.update();
+ mCallback.update(getInterfaceLinkState());
}
private void processPref64Option(StructNdOptPref64 opt, final long now) {