Snap for 8005954 from 41014a3e6626d070d83b050826dbdfe0679dd40d to sdk-release Change-Id: I41accb35da54977341660b7623e774a1cc1bf0df
diff --git a/common/networkstackclient/Android.bp b/common/networkstackclient/Android.bp index fd7bfc9..3c97dc2 100644 --- a/common/networkstackclient/Android.bp +++ b/common/networkstackclient/Android.bp
@@ -35,7 +35,6 @@ apex_available: [ "//apex_available:platform", "com.android.wifi", - "com.android.bluetooth.updatable", "com.android.tethering", ], // this is part of updatable modules(NetworkStack) which targets 29(Q) @@ -109,7 +108,6 @@ java: { apex_available: [ "//apex_available:platform", - "com.android.bluetooth.updatable", "com.android.wifi", "com.android.tethering", ],
diff --git a/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java b/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java index d3e6cb7..7cf46f5 100644 --- a/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java +++ b/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java
@@ -141,5 +141,9 @@ * * @param lossInfo the specific neighbor reachability loss information. */ - public void onReachabilityFailure(ReachabilityLossInfoParcelable lossInfo) {} + public void onReachabilityFailure(ReachabilityLossInfoParcelable lossInfo) { + // If the client does not implement this method, call the older + // onReachabilityLost method. + onReachabilityLost(lossInfo.message); + } }
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java index 6433e04..704ca7d 100644 --- a/src/android/net/dhcp/DhcpClient.java +++ b/src/android/net/dhcp/DhcpClient.java
@@ -251,6 +251,7 @@ public static final int DHCP_SUCCESS = 1; public static final int DHCP_FAILURE = 2; public static final int DHCP_IPV6_ONLY = 3; + public static final int DHCP_REFRESH_FAILURE = 4; // Internal messages. private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100; @@ -391,6 +392,7 @@ private State mIpAddressConflictDetectingState = new IpAddressConflictDetectingState(); private State mDhcpDecliningState = new DhcpDecliningState(); private State mIpv6OnlyWaitState = new Ipv6OnlyWaitState(); + private State mDhcpRefreshingAddressState = new DhcpRefreshingAddressState(); private WakeupMessage makeWakeupMessage(String cmdName, int cmd) { cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName; @@ -499,6 +501,7 @@ addState(mDhcpRenewingState, mDhcpHaveLeaseState); addState(mDhcpRebindingState, mDhcpHaveLeaseState); addState(mDhcpDecliningState, mDhcpHaveLeaseState); + addState(mDhcpRefreshingAddressState, mDhcpHaveLeaseState); addState(mDhcpInitRebootState, mDhcpState); addState(mDhcpRebootingState, mDhcpState); // CHECKSTYLE:ON IndentationCheck @@ -747,9 +750,14 @@ } private boolean sendDiscoverPacket() { + // When Rapid Commit option is enabled, limit only the first 3 DHCPDISCOVER packets + // taking Rapid Commit option, in order to prevent the potential interoperability issue + // and be able to rollback later. See {@link DHCP_TIMEOUT_MS} for the (re)transmission + // schedule with 10% jitter. + final boolean requestRapidCommit = isDhcpRapidCommitEnabled() && (getSecs() <= 4); final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, - DO_UNICAST, getRequestedParams(), isDhcpRapidCommitEnabled(), mHostname, + DO_UNICAST, getRequestedParams(), requestRapidCommit, mHostname, mConfiguration.options); mMetrics.incrementCountForDiscover(); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); @@ -844,11 +852,11 @@ CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease)); } - private void notifyFailure() { + private void notifyFailure(int arg) { if (isDhcpLeaseCacheEnabled()) { setLeaseExpiredToIpMemoryStore(); } - mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null); + mController.sendMessage(CMD_POST_DHCP_ACTION, arg, 0, null); } private void acceptDhcpResults(DhcpResults results, String msg) { @@ -1044,7 +1052,7 @@ if (mDhcpPacketHandler.start()) return; Log.e(TAG, "Fail to start DHCP Packet Handler"); } - notifyFailure(); + notifyFailure(DHCP_FAILURE); // We cannot call transitionTo because a transition is still in progress. // Instead, ensure that we process CMD_STOP_DHCP as soon as the transition is complete. deferMessage(obtainMessage(CMD_STOP_DHCP)); @@ -1449,7 +1457,7 @@ switch (message.what) { case CMD_EXPIRE_DHCP: Log.d(TAG, "Lease expired!"); - notifyFailure(); + notifyFailure(DHCP_FAILURE); transitionTo(mStoppedState); return HANDLED; default: @@ -1761,7 +1769,7 @@ // return an IPv4 address from another interface, or even return "0.0.0.0". // // TODO: Consider deleting this check, following testing on several kernels. - notifyFailure(); + notifyFailure(DHCP_FAILURE); transitionTo(mStoppedState); } @@ -1783,7 +1791,7 @@ preDhcpTransitionTo(mWaitBeforeRenewalState, mDhcpRenewingState); return HANDLED; case CMD_REFRESH_LINKADDRESS: - transitionTo(mDhcpRebindingState); + transitionTo(mDhcpRefreshingAddressState); return HANDLED; default: return NOT_HANDLED; @@ -1811,6 +1819,10 @@ protected abstract Inet4Address packetDestination(); + // Check whether DhcpClient should notify provisioning failure when receiving DHCPNAK + // in renew/rebind state or just restart reconfiguration from StoppedState. + protected abstract boolean shouldRestartOnNak(); + protected boolean sendPacket() { return sendRequestPacket( (Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr @@ -1829,7 +1841,7 @@ if (results != null) { if (!mDhcpLease.ipAddress.equals(results.ipAddress)) { Log.d(TAG, "Renewed lease not for our current IP address!"); - notifyFailure(); + notifyFailure(DHCP_FAILURE); transitionTo(mStoppedState); return; } @@ -1845,7 +1857,7 @@ } } else if (packet instanceof DhcpNakPacket) { Log.d(TAG, "Received NAK, returning to StoppedState"); - notifyFailure(); + notifyFailure(shouldRestartOnNak() ? DHCP_REFRESH_FAILURE : DHCP_FAILURE); transitionTo(mStoppedState); } } @@ -1878,6 +1890,11 @@ return (mDhcpLease.serverAddress != null) ? mDhcpLease.serverAddress : INADDR_BROADCAST; } + + @Override + protected boolean shouldRestartOnNak() { + return false; + } } class DhcpRebindingState extends DhcpReacquiringState { @@ -1902,6 +1919,22 @@ protected Inet4Address packetDestination() { return INADDR_BROADCAST; } + + @Override + protected boolean shouldRestartOnNak() { + return false; + } + } + + class DhcpRefreshingAddressState extends DhcpRebindingState { + DhcpRefreshingAddressState() { + mLeaseMsg = "Refreshing address"; + } + + @Override + protected boolean shouldRestartOnNak() { + return true; + } } class DhcpInitRebootState extends DhcpRequestingState {
diff --git a/src/android/net/dhcp/DhcpPacket.java b/src/android/net/dhcp/DhcpPacket.java index 76dc807..63c5b0c 100644 --- a/src/android/net/dhcp/DhcpPacket.java +++ b/src/android/net/dhcp/DhcpPacket.java
@@ -321,7 +321,8 @@ * packet may include this option. */ public static final byte DHCP_RAPID_COMMIT = 80; - protected boolean mRapidCommit; + @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) + public boolean mRapidCommit; /** * DHCP IPv6-Only Preferred Option(RFC 8925).
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java index a81ab5c..00aa599 100644 --- a/src/android/net/ip/IpClient.java +++ b/src/android/net/ip/IpClient.java
@@ -21,6 +21,8 @@ import static android.net.ip.IIpClient.PROV_IPV4_DISABLED; import static android.net.ip.IIpClient.PROV_IPV6_DISABLED; import static android.net.ip.IIpClient.PROV_IPV6_LINKLOCAL; +import static android.net.ip.IpReachabilityMonitor.INVALID_REACHABILITY_LOSS_TYPE; +import static android.net.ip.IpReachabilityMonitor.nudEventTypeToInt; import static android.net.util.NetworkStackUtils.IPCLIENT_DISABLE_ACCEPT_RA_VERSION; import static android.net.util.NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION; import static android.net.util.NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION; @@ -63,6 +65,8 @@ import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpManagerEvent; import android.net.networkstack.aidl.dhcp.DhcpOption; +import android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable; +import android.net.networkstack.aidl.ip.ReachabilityLossReason; import android.net.shared.InitialConfiguration; import android.net.shared.Layer2Information; import android.net.shared.ProvisioningConfiguration; @@ -81,6 +85,7 @@ import android.os.SystemClock; import android.stats.connectivity.DisconnectCode; import android.stats.connectivity.NetworkQuirkEvent; +import android.stats.connectivity.NudEventType; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; @@ -316,6 +321,11 @@ /** * Called when the internal IpReachabilityMonitor (if enabled) has detected the loss of * required neighbors (e.g. on-link default gw or dns servers) due to NUD_FAILED. + * + * Note this method is only supported on networkstack-aidl-interfaces-v12 or below. + * For above aidl versions, the caller should call {@link onReachabilityFailure} instead. + * For callbacks extending IpClientCallbacks, this method will be called iff the callback + * does not implement onReachabilityFailure. */ public void onReachabilityLost(String logMsg) { log("onReachabilityLost(" + logMsg + ")"); @@ -402,6 +412,20 @@ } /** + * Called when Neighbor Unreachability Detection fails, that might be caused by the organic + * probe or probeAll from IpReachabilityMonitor (if enabled). + */ + public void onReachabilityFailure(ReachabilityLossInfoParcelable lossInfo) { + log("onReachabilityFailure(" + lossInfo.message + ", loss reason: " + + reachabilityLossReasonToString(lossInfo.reason) + ")"); + try { + mCallback.onReachabilityFailure(lossInfo); + } catch (RemoteException e) { + log("Failed to call onReachabilityFailure", e); + } + } + + /** * Get the version of the IIpClientCallbacks AIDL interface. */ public int getInterfaceVersion() { @@ -478,6 +502,10 @@ private static final int PROV_CHANGE_GAINED_PROVISIONING = 3; private static final int PROV_CHANGE_STILL_PROVISIONED = 4; + // onReachabilityFailure callback is added since networkstack-aidl-interfaces-v13. + @VisibleForTesting + static final int VERSION_ADDED_REACHABILITY_FAILURE = 13; + // Specific vendor OUI(3 bytes)/vendor specific type(1 byte) pattern for upstream hotspot // device detection. Add new byte array pattern below in turn. private static final List<byte[]> METERED_IE_PATTERN_LIST = Collections.unmodifiableList( @@ -1250,6 +1278,20 @@ transitionTo(mStoppingState); } + // Convert reachability loss reason enum to a string. + private static String reachabilityLossReasonToString(int reason) { + switch (reason) { + case ReachabilityLossReason.ROAM: + return "reachability_loss_after_roam"; + case ReachabilityLossReason.CONFIRM: + return "reachability_loss_after_confirm"; + case ReachabilityLossReason.ORGANIC: + return "reachability_loss_organic"; + default: + return "unknown"; + } + } + private static boolean hasIpv6LinkLocalInterfaceRoute(final LinkProperties lp) { for (RouteInfo r : lp.getRoutes()) { if (r.getDestination().equals(new IpPrefix("fe80::/64")) @@ -1859,8 +1901,17 @@ mLog, new IpReachabilityMonitor.Callback() { @Override - public void notifyLost(InetAddress ip, String logMsg) { - mCallback.onReachabilityLost(logMsg); + 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, @@ -1938,18 +1989,18 @@ // If the BSSID has not changed, there is nothing to do. if (info.bssid.equals(mCurrentBssid)) return; - // Before trigger probing to the interesting neighbors, send Gratuitous ARP + // Before trigger probing to the critical neighbors, send Gratuitous ARP // and Neighbor Advertisment in advance to propgate host's IPv4/v6 addresses. if (isGratuitousArpNaRoamingEnabled()) { maybeSendGratuitousARP(mLinkProperties); maybeSendGratuitousNAs(mLinkProperties, true /* isGratuitousNaAfterRoaming */); } - if (mIpReachabilityMonitor != null) { - mIpReachabilityMonitor.probeAll(true /* dueToRoam */); - } - - // Check whether to refresh previous IP lease on L2 roaming happened. + // Check whether attempting to refresh previous IP lease on specific networks or need to + // probe the critical neighbors proactively on L2 roaming happened. The NUD probe on the + // specific networks is cancelled because otherwise the probe will happen in parallel with + // DHCP refresh, it will be difficult to understand what happened exactly and error-prone + // to introduce race condition. final String ssid = removeDoubleQuotes(mConfiguration.mDisplayName); if (DHCP_ROAMING_SSID_SET.contains(ssid) && mDhcpClient != null) { if (DBG) { @@ -1959,6 +2010,8 @@ + " , starting refresh leased IP address"); } mDhcpClient.sendMessage(DhcpClient.CMD_REFRESH_LINKADDRESS); + } else if (mIpReachabilityMonitor != null) { + mIpReachabilityMonitor.probeAll(true /* dueToRoam */); } mCurrentBssid = info.bssid; } @@ -2549,6 +2602,20 @@ break; case DhcpClient.DHCP_IPV6_ONLY: break; + case DhcpClient.DHCP_REFRESH_FAILURE: + // This case should only happen on the receipt of DHCPNAK when + // refreshing IP address post L2 roaming on some specific networks. + // WiFi should try to restart a new provisioning immediately without + // disconnecting L2 when it receives DHCP roaming failure event. IPv4 + // link address still will be cleared when DhcpClient transits to + // StoppedState from RefreshingAddress State, although it will result + // in a following onProvisioningFailure then, WiFi should ignore this + // failure and start a new DHCP reconfiguration from INIT state. + final ReachabilityLossInfoParcelable lossInfo = + new ReachabilityLossInfoParcelable("DHCP refresh failure", + ReachabilityLossReason.ROAM); + mCallback.onReachabilityFailure(lossInfo); + break; default: logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1); }
diff --git a/src/android/net/ip/IpReachabilityMonitor.java b/src/android/net/ip/IpReachabilityMonitor.java index 6b883f3..c716fdf 100644 --- a/src/android/net/ip/IpReachabilityMonitor.java +++ b/src/android/net/ip/IpReachabilityMonitor.java
@@ -32,6 +32,7 @@ import android.net.ip.IpNeighborMonitor.NeighborEventConsumer; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpReachabilityEvent; +import android.net.networkstack.aidl.ip.ReachabilityLossReason; import android.net.util.InterfaceParams; import android.net.util.SharedLog; import android.os.ConditionVariable; @@ -157,6 +158,7 @@ private static final int INVALID_NUD_MCAST_RESOLICIT_NUM = -1; private static final int INVALID_LEGACY_NUD_FAILURE_TYPE = -1; + public static final int INVALID_REACHABILITY_LOSS_TYPE = -1; public interface Callback { /** @@ -165,7 +167,7 @@ * * TODO: refactor to something like notifyProvisioningLost(String msg). */ - void notifyLost(InetAddress ip, String logMsg); + void notifyLost(InetAddress ip, String logMsg, NudEventType type); } /** @@ -408,10 +410,11 @@ final String logMsg = "ALERT neighbor: " + event.ip + " MAC address changed from: " + prev.macAddr + " to: " + event.macAddr; + final NudEventType type = + getMacAddressChangedEventType(isFromProbe(), isNudFailureDueToRoam()); mLog.w(logMsg); - mCallback.notifyLost(event.ip, logMsg); - logNudFailed(event, - getMacAddressChangedEventType(isFromProbe(), isNudFailureDueToRoam())); + mCallback.notifyLost(event.ip, logMsg, type); + logNudFailed(event, type); return; } maybeRestoreNeighborParameters(); @@ -458,7 +461,7 @@ Log.w(TAG, logMsg); // TODO: remove |ip| when the callback signature no longer has // an InetAddress argument. - mCallback.notifyLost(ip, logMsg); + mCallback.notifyLost(ip, logMsg, type); } logNudFailed(event, type); } @@ -520,7 +523,9 @@ private long getProbeWakeLockDuration() { final long gracePeriodMs = 500; - return (long) (mNumSolicits * mInterSolicitIntervalMs) + gracePeriodMs; + final int numSolicits = + mNumSolicits + (isMulticastResolicitEnabled() ? NUD_MCAST_RESOLICIT_NUM : 0); + return (long) (numSolicits * mInterSolicitIntervalMs) + gracePeriodMs; } private void setNeighbourParametersPostRoaming() { @@ -571,7 +576,7 @@ } } - mNumSolicits = isMulticastResolicitEnabled() ? (numSolicits + numResolicits) : numSolicits; + mNumSolicits = numSolicits; mInterSolicitIntervalMs = interSolicitIntervalMs; } @@ -691,4 +696,24 @@ return INVALID_LEGACY_NUD_FAILURE_TYPE; } } + + /** + * Convert the NUD critical failure event type to a int constant defined in IIpClientCallbacks. + */ + public static int nudEventTypeToInt(final NudEventType type) { + switch (type) { + case NUD_POST_ROAMING_FAILED_CRITICAL: + case NUD_POST_ROAMING_MAC_ADDRESS_CHANGED: + return ReachabilityLossReason.ROAM; + case NUD_CONFIRM_FAILED_CRITICAL: + case NUD_CONFIRM_MAC_ADDRESS_CHANGED: + return ReachabilityLossReason.CONFIRM; + case NUD_ORGANIC_FAILED_CRITICAL: + case NUD_ORGANIC_MAC_ADDRESS_CHANGED: + return ReachabilityLossReason.ORGANIC; + // For other NudEventType which won't trigger notifyLost, just ignore these events. + default: + return INVALID_REACHABILITY_LOSS_TYPE; + } + } }
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt b/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt index 748ee5a..fa379d3 100644 --- a/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt +++ b/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt
@@ -21,8 +21,6 @@ import android.net.ipmemorystore.Status import android.net.ipmemorystore.Status.SUCCESS import android.util.ArrayMap -import java.net.Inet6Address -import kotlin.test.assertEquals import org.mockito.Mockito.any import org.mockito.Mockito.doAnswer import org.mockito.ArgumentCaptor @@ -63,17 +61,6 @@ verify(mIpMemoryStore, never()).storeNetworkAttributes(eq(l2Key), any(), any()) } - override fun assertNotifyNeighborLost(targetIp: Inet6Address) { - val target = ArgumentCaptor.forClass(Inet6Address::class.java) - - verify(mCallback, timeout(TEST_TIMEOUT_MS)).notifyLost(target.capture(), any()) - assertEquals(targetIp, target.getValue()) - } - - override fun assertNeverNotifyNeighborLost() { - verify(mCallback, never()).notifyLost(any(), any()) - } - override fun storeNetworkAttributes(l2Key: String, na: NetworkAttributes) { doAnswer { inv -> val listener = inv.getArgument<OnNetworkAttributesRetrievedListener>(1)
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java index 378c733..22a4844 100644 --- a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java +++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -29,8 +29,8 @@ import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable; import static android.net.ip.IpReachabilityMonitor.MIN_NUD_SOLICIT_NUM; import static android.net.ip.IpReachabilityMonitor.NUD_MCAST_RESOLICIT_NUM; +import static android.net.ip.IpReachabilityMonitor.nudEventTypeToInt; import static android.net.ipmemorystore.Status.SUCCESS; -import static android.net.shared.ProvisioningConfiguration.VERSION_ADDED_PROVISIONING_ENUM; import static android.system.OsConstants.ETH_P_IPV6; import static android.system.OsConstants.IFA_F_TEMPORARY; import static android.system.OsConstants.IPPROTO_ICMPV6; @@ -71,6 +71,7 @@ import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.longThat; import static org.mockito.Mockito.any; @@ -124,6 +125,8 @@ import android.net.ipmemorystore.Status; import android.net.networkstack.TestNetworkStackServiceClient; import android.net.networkstack.aidl.dhcp.DhcpOption; +import android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable; +import android.net.networkstack.aidl.ip.ReachabilityLossReason; import android.net.shared.Layer2Information; import android.net.shared.ProvisioningConfiguration; import android.net.shared.ProvisioningConfiguration.ScanResultInfo; @@ -140,6 +143,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.stats.connectivity.NetworkQuirkEvent; +import android.stats.connectivity.NudEventType; import android.system.ErrnoException; import android.system.Os; @@ -155,6 +159,7 @@ import com.android.net.module.util.structs.LlaOption; import com.android.net.module.util.structs.PrefixInformationOption; import com.android.net.module.util.structs.RdnssOption; +import com.android.networkstack.R; import com.android.networkstack.apishim.CaptivePortalDataShimImpl; import com.android.networkstack.apishim.ConstantsShim; import com.android.networkstack.apishim.common.ShimUtils; @@ -290,7 +295,6 @@ @Mock protected NetworkStackIpMemoryStore mIpMemoryStore; @Mock private NetworkQuirkMetrics.Dependencies mNetworkQuirkMetricsDeps; @Mock private IpReachabilityMonitorMetrics mIpReachabilityMonitorMetrics; - @Mock protected IpReachabilityMonitor.Callback mCallback; @Spy private INetd mNetd; private NetworkObserverRegistry mNetworkObserverRegistry; @@ -436,7 +440,7 @@ InterfaceParams ifParams, Handler h, SharedLog log, IpReachabilityMonitor.Callback callback, boolean usingMultinetworkPolicyTracker, IpReachabilityMonitor.Dependencies deps, final INetd netd) { - return new IpReachabilityMonitor(context, ifParams, h, log, mCallback, + return new IpReachabilityMonitor(context, ifParams, h, log, callback, usingMultinetworkPolicyTracker, deps, netd); } @@ -540,10 +544,6 @@ protected abstract void assertIpMemoryNeverStoreNetworkAttributes(String l2Key, long timeout); - protected abstract void assertNotifyNeighborLost(Inet6Address targetIp); - - protected abstract void assertNeverNotifyNeighborLost(); - protected final boolean testSkipped() { // TODO: split out a test suite for root tests, and fail hard instead of skipping the test // if it is run on devices where TestNetworkStackServiceClient is not supported @@ -592,6 +592,8 @@ if (useNetworkStackSignature()) { setUpMocks(); setUpIpClient(); + // Enable packet retransmit alarm in DhcpClient. + enableRealAlarm("DhcpClient." + mIfaceName + ".KICK"); } mIIpClient = makeIIpClient(mIfaceName, mCb); @@ -604,10 +606,17 @@ when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm); when(mContext.getResources()).thenReturn(mResources); + when(mResources.getInteger(eq(R.integer.config_nud_postroaming_solicit_num))).thenReturn(5); + when(mResources.getInteger(eq(R.integer.config_nud_postroaming_solicit_interval))) + .thenReturn(750); + when(mResources.getInteger(eq(R.integer.config_nud_steadystate_solicit_num))) + .thenReturn(10); + when(mResources.getInteger(eq(R.integer.config_nud_steadystate_solicit_interval))) + .thenReturn(750); when(mContext.getContentResolver()).thenReturn(mContentResolver); when(mNetworkStackServiceManager.getIpMemoryStoreService()) .thenReturn(mIpMemoryStoreService); - when(mCb.getInterfaceVersion()).thenReturn(VERSION_ADDED_PROVISIONING_ENUM); + when(mCb.getInterfaceVersion()).thenReturn(IpClient.VERSION_ADDED_REACHABILITY_FAILURE); mDependencies.setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67); mDependencies.setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10); @@ -688,6 +697,17 @@ } } + private void enableRealAlarm(String cmdName) { + doAnswer((inv) -> { + final Context context = InstrumentationRegistry.getTargetContext(); + final AlarmManager alarmManager = context.getSystemService(AlarmManager.class); + alarmManager.setExact(inv.getArgument(0), inv.getArgument(1), inv.getArgument(2), + inv.getArgument(3), inv.getArgument(4)); + return null; + }).when(mAlarm).setExact(anyInt(), anyLong(), eq(cmdName), any(OnAlarmListener.class), + any(Handler.class)); + } + private IpClient makeIpClient() throws Exception { IpClient ipc = new IpClient(mContext, mIfaceName, mCb, mNetworkObserverRegistry, mNetworkStackServiceManager, mDependencies); @@ -1388,6 +1408,27 @@ assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); } + @Test + public void testRollbackFromRapidCommitOption() throws Exception { + startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, + true /* isDhcpRapidCommitEnabled */, false /* isPreConnectionEnabled */, + false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + + final List<DhcpPacket> discoverList = new ArrayList<DhcpPacket>(); + DhcpPacket packet; + do { + packet = getNextDhcpPacket(); + assertTrue(packet instanceof DhcpDiscoverPacket); + discoverList.add(packet); + } while (discoverList.size() < 4); + + // Check the only first 3 DHCPDISCOVERs take rapid commit option. + assertTrue(discoverList.get(0).mRapidCommit); + assertTrue(discoverList.get(1).mRapidCommit); + assertTrue(discoverList.get(2).mRapidCommit); + assertFalse(discoverList.get(3).mRapidCommit); + } + @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") public void testDhcpClientStartWithCachedInfiniteLease() throws Exception { final DhcpPacket packet = getReplyFromDhcpLease( @@ -2511,12 +2552,23 @@ mPacketReader.sendResponse(packetBuffer); HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); - if (shouldReplyNakOnRoam || hasMismatchedIpAddress) { - // notifyFailure - ArgumentCaptor<DhcpResultsParcelable> captor = + if (shouldReplyNakOnRoam) { + ArgumentCaptor<ReachabilityLossInfoParcelable> lossInfoCaptor = + ArgumentCaptor.forClass(ReachabilityLossInfoParcelable.class); + verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityFailure(lossInfoCaptor.capture()); + assertEquals(ReachabilityLossReason.ROAM, lossInfoCaptor.getValue().reason); + + // IPv4 address will be still deleted when DhcpClient state machine exits from + // DhcpHaveLeaseState, a following onProvisioningFailure will be thrown then. + // Also check DhcpClient won't send any DHCPDISCOVER packet. + verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any()); + assertNull(getNextDhcpPacket(TEST_TIMEOUT_MS)); + verify(mCb, never()).onNewDhcpResults(any()); + } else if (hasMismatchedIpAddress) { + ArgumentCaptor<DhcpResultsParcelable> resultsCaptor = ArgumentCaptor.forClass(DhcpResultsParcelable.class); - verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(captor.capture()); - DhcpResults lease = fromStableParcelable(captor.getValue()); + verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(resultsCaptor.capture()); + DhcpResults lease = fromStableParcelable(resultsCaptor.getValue()); assertNull(lease); // DhcpClient rolls back to StoppedState instead of INIT state after calling @@ -3424,6 +3476,38 @@ prepareIpReachabilityMonitorTest(false /* isMulticastResolicitEnabled */); } + private void assertNotifyNeighborLost(Inet6Address targetIp, NudEventType eventType) + throws Exception { + // For root test suite, rely on the IIpClient aidl interface version constant defined in + // {@link IpClientRootTest.BinderCbWrapper}; for privileged integration test suite that + // requires signature permission, use the mocked aidl version defined in {@link setUpMocks}, + // which results in only new callbacks are verified. And add separate test cases to test the + // legacy callbacks explicitly as well. + assertNeighborReachabilityLoss(targetIp, eventType, + useNetworkStackSignature() + ? IpClient.VERSION_ADDED_REACHABILITY_FAILURE + : mIIpClient.getInterfaceVersion()); + } + + private void assertNeighborReachabilityLoss(Inet6Address targetIp, NudEventType eventType, + int targetAidlVersion) throws Exception { + if (targetAidlVersion >= IpClient.VERSION_ADDED_REACHABILITY_FAILURE) { + final ArgumentCaptor<ReachabilityLossInfoParcelable> lossInfoCaptor = + ArgumentCaptor.forClass(ReachabilityLossInfoParcelable.class); + verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityFailure(lossInfoCaptor.capture()); + assertEquals(nudEventTypeToInt(eventType), lossInfoCaptor.getValue().reason); + verify(mCb, never()).onReachabilityLost(any()); + } else { + verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(any()); + verify(mCb, never()).onReachabilityFailure(any()); + } + } + + private void assertNeverNotifyNeighborLost() throws Exception { + verify(mCb, never()).onReachabilityFailure(any()); + verify(mCb, never()).onReachabilityLost(any()); + } + private void prepareIpReachabilityMonitorTest(boolean isMulticastResolicitEnabled) throws Exception { final ScanResultInfo info = makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID); @@ -3444,8 +3528,7 @@ forceLayer2Roaming(); } - @Test - public void testIpReachabilityMonitor_probeFailed() throws Exception { + private void runIpReachabilityMonitorProbeFailedTest() throws Exception { prepareIpReachabilityMonitorTest(); final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations(); @@ -3454,7 +3537,22 @@ assertUnicastNeighborSolicitation(ns, ROUTER_MAC /* dstMac */, ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */); } - assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */); + } + + @Test + public void testIpReachabilityMonitor_probeFailed() throws Exception { + runIpReachabilityMonitorProbeFailedTest(); + assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */, + NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL); + } + + @Test @SignatureRequiredTest(reason = "requires mock callback object") + public void testIpReachabilityMonitor_probeFailed_legacyCallback() throws Exception { + when(mCb.getInterfaceVersion()).thenReturn(12 /* assign an older interface aidl version */); + + runIpReachabilityMonitorProbeFailedTest(); + verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(any()); + verify(mCb, never()).onReachabilityFailure(any()); } @Test @@ -3473,8 +3571,7 @@ assertNeverNotifyNeighborLost(); } - @Test - public void testIpReachabilityMonitor_mcastResoclicitProbeFailed() throws Exception { + private void runIpReachabilityMonitorMcastResolicitProbeFailedTest() throws Exception { prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */); final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations(); @@ -3487,11 +3584,27 @@ for (NeighborSolicitation ns : nsList.subList(MIN_NUD_SOLICIT_NUM, nsList.size())) { assertMulticastNeighborSolicitation(ns, ROUTER_LINK_LOCAL /* targetIp */); } - assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */); } @Test - public void testIpReachabilityMonitor_mcastResoclicitProbeReachableWithSameLinkLayerAddress() + public void testIpReachabilityMonitor_mcastResolicitProbeFailed() throws Exception { + runIpReachabilityMonitorMcastResolicitProbeFailedTest(); + assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */, + NudEventType.NUD_POST_ROAMING_FAILED_CRITICAL); + } + + @Test @SignatureRequiredTest(reason = "requires mock callback object") + public void testIpReachabilityMonitor_mcastResolicitProbeFailed_legacyCallback() + throws Exception { + when(mCb.getInterfaceVersion()).thenReturn(12 /* assign an older interface aidl version */); + + runIpReachabilityMonitorMcastResolicitProbeFailedTest(); + verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(any()); + verify(mCb, never()).onReachabilityFailure(any()); + } + + @Test + public void testIpReachabilityMonitor_mcastResolicitProbeReachableWithSameLinkLayerAddress() throws Exception { prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */); @@ -3508,7 +3621,7 @@ } @Test - public void testIpReachabilityMonitor_mcastResoclicitProbeReachableWithDiffLinkLayerAddress() + public void testIpReachabilityMonitor_mcastResolicitProbeReachableWithDiffLinkLayerAddress() throws Exception { prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */); @@ -3527,7 +3640,8 @@ ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */, ns.ipv6Hdr.srcIp /* dstIp */, flag, ROUTER_LINK_LOCAL /* target */); mPacketReader.sendResponse(na); - assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */); + assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */, + NudEventType.NUD_POST_ROAMING_MAC_ADDRESS_CHANGED); } @Test
diff --git a/tests/integration/src/android/net/ip/IpClientRootTest.kt b/tests/integration/src/android/net/ip/IpClientRootTest.kt index d861639..7359e05 100644 --- a/tests/integration/src/android/net/ip/IpClientRootTest.kt +++ b/tests/integration/src/android/net/ip/IpClientRootTest.kt
@@ -33,7 +33,6 @@ import androidx.test.platform.app.InstrumentationRegistry import com.android.net.module.util.DeviceConfigUtils import java.lang.System.currentTimeMillis -import java.net.Inet6Address import java.util.concurrent.CompletableFuture import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -46,8 +45,6 @@ import org.junit.AfterClass import org.junit.BeforeClass import org.mockito.ArgumentCaptor -import org.mockito.Mockito.anyString -import org.mockito.Mockito.never import org.mockito.Mockito.timeout import org.mockito.Mockito.verify @@ -265,14 +262,6 @@ assertNull(listener.getBlockingNetworkAttributes(timeout)) } - override fun assertNotifyNeighborLost(targetIp: Inet6Address) { - verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(anyString()) - } - - override fun assertNeverNotifyNeighborLost() { - verify(mCb, never()).onReachabilityLost(anyString()) - } - override fun storeNetworkAttributes(l2Key: String, na: NetworkAttributes) { mStore.storeNetworkAttributes(l2Key, na, null /* listener */) }
diff --git a/tests/integration/src/android/net/networkstack/TestNetworkStackServiceClient.kt b/tests/integration/src/android/net/networkstack/TestNetworkStackServiceClient.kt index bc3e5e0..47936ac 100644 --- a/tests/integration/src/android/net/networkstack/TestNetworkStackServiceClient.kt +++ b/tests/integration/src/android/net/networkstack/TestNetworkStackServiceClient.kt
@@ -33,7 +33,7 @@ companion object { private val context by lazy { InstrumentationRegistry.getInstrumentation().context } private val networkStackVersion by lazy { - val component = getNetworkStackComponent() + val component = getNetworkStackComponent(INetworkStackConnector::class.java.name) val info = context.packageManager.getPackageInfo(component.packageName, 0 /* flags */) info.longVersionCode } @@ -54,8 +54,8 @@ return networkStackVersion == 300000000L || networkStackVersion >= 301100000L } - private fun getNetworkStackComponent(): ComponentName { - val connectorIntent = Intent(INetworkStackConnector::class.java.name) + private fun getNetworkStackComponent(connectorAction: String): ComponentName { + val connectorIntent = Intent(connectorAction) return connectorIntent.resolveSystemService(context.packageManager, MATCH_SYSTEM_ONLY) ?: fail("TestNetworkStackService not found") } @@ -71,7 +71,7 @@ private fun init() { val bindIntent = Intent(INetworkStackConnector::class.java.name + ".Test") - bindIntent.component = getNetworkStackComponent() + bindIntent.component = getNetworkStackComponent(bindIntent.action) context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE) }
diff --git a/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt b/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt index 5245cc3..246f000 100644 --- a/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt +++ b/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.kt
@@ -287,10 +287,15 @@ reachabilityMonitor.updateLinkProperties(TEST_LINK_PROPERTIES) neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_DNS, NUD_FAILED)) - verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(TEST_IPV4_DNS), anyString()) + verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(TEST_IPV4_DNS), anyString(), + eq(NUD_ORGANIC_FAILED_CRITICAL)) } - private fun runLoseProvisioningTest(newLp: LinkProperties, lostNeighbor: InetAddress) { + private fun runLoseProvisioningTest( + newLp: LinkProperties, + lostNeighbor: InetAddress, + eventType: NudEventType + ) { reachabilityMonitor.updateLinkProperties(newLp) neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV4_GATEWAY, NUD_STALE)) @@ -299,7 +304,8 @@ neighborMonitor.enqueuePacket(makeNewNeighMessage(TEST_IPV6_DNS, NUD_STALE)) neighborMonitor.enqueuePacket(makeNewNeighMessage(lostNeighbor, NUD_FAILED)) - verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(lostNeighbor), anyString()) + verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(lostNeighbor), anyString(), + eq(eventType)) } private fun verifyNudFailureMetrics( @@ -327,7 +333,7 @@ neighborMonitor.enqueuePacket(makeNewNeighMessage(lostNeighbor, NUD_FAILED)) handlerThread.waitForIdle(TEST_TIMEOUT_MS) - verify(callback, never()).notifyLost(any(), anyString()) + verify(callback, never()).notifyLost(any(), anyString(), any(NudEventType::class.java)) verifyNudFailureMetrics(eventType, ipType, lostNeighborType) } @@ -343,27 +349,30 @@ neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE, "001122334455" /* oldMac */)) handlerThread.waitForIdle(TEST_TIMEOUT_MS) - verify(callback, never()).notifyLost(eq(neighbor), anyString()) + verify(callback, never()).notifyLost(eq(neighbor), anyString(), + any(NudEventType::class.java)) } @Test fun testLoseProvisioning_Ipv4DnsLost() { - runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS) + runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_DNS, NUD_ORGANIC_FAILED_CRITICAL) } @Test fun testLoseProvisioning_Ipv6DnsLost() { - runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_DNS) + runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_DNS, NUD_ORGANIC_FAILED_CRITICAL) } @Test fun testLoseProvisioning_Ipv4GatewayLost() { - runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY) + runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV4_GATEWAY, + NUD_ORGANIC_FAILED_CRITICAL) } @Test fun testLoseProvisioning_Ipv6GatewayLost() { - runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY) + runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, + NUD_ORGANIC_FAILED_CRITICAL) } private fun runNudProbeFailureMetricsTest( @@ -373,7 +382,7 @@ ipType: IpType, lostNeighborType: NudNeighborType ) { - runLoseProvisioningTest(lp, lostNeighbor) + runLoseProvisioningTest(lp, lostNeighbor, eventType) verifyNudFailureMetrics(eventType, ipType, lostNeighborType) } @@ -526,7 +535,8 @@ handlerThread.waitForIdle(TEST_TIMEOUT_MS) Thread.sleep(2) reachabilityMonitor.probeAll(false /* dueToRoam */) - runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY) + runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, + NUD_POST_ROAMING_FAILED_CRITICAL) verifyNudFailureMetrics(NUD_POST_ROAMING_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY) } @@ -537,7 +547,8 @@ handlerThread.waitForIdle(TEST_TIMEOUT_MS) Thread.sleep(2) reachabilityMonitor.probeAll(true /* dueToRoam */) - runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY) + runLoseProvisioningTest(TEST_LINK_PROPERTIES, TEST_IPV6_GATEWAY, + NUD_CONFIRM_FAILED_CRITICAL) verifyNudFailureMetrics(NUD_CONFIRM_FAILED_CRITICAL, IPV6, NUD_NEIGHBOR_GATEWAY) } @@ -549,7 +560,8 @@ ) { neighborMonitor.enqueuePacket(makeNewNeighMessage(neighbor, NUD_REACHABLE, "1122334455aa" /* newMac */)) - verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(neighbor), anyString()) + verify(callback, timeout(TEST_TIMEOUT_MS)).notifyLost(eq(neighbor), anyString(), + eq(eventType)) verifyNudFailureMetrics(eventType, ipType, NUD_NEIGHBOR_GATEWAY) }