Merge "Add ConnectivitySettingsManagerTest"
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index d3b9393..6031646 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -37,6 +37,7 @@
"networkstack-client",
"android.hardware.tetheroffload.config-V1.0-java",
"android.hardware.tetheroffload.control-V1.0-java",
+ "android.hardware.tetheroffload.control-V1.1-java",
"net-utils-framework-common",
"net-utils-device-common",
"netd-client",
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
index 88c77b0..44e3916 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
@@ -26,6 +26,7 @@
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
import android.annotation.NonNull;
@@ -96,7 +97,8 @@
private final SharedLog mLog;
private final HashMap<String, LinkProperties> mDownstreams;
private boolean mConfigInitialized;
- private boolean mControlInitialized;
+ @OffloadHardwareInterface.OffloadHalVersion
+ private int mControlHalVersion;
private LinkProperties mUpstreamLinkProperties;
// The complete set of offload-exempt prefixes passed in via Tethering from
// all upstream and downstream sources.
@@ -179,7 +181,7 @@
}
}
- mControlInitialized = mHwInterface.initOffloadControl(
+ mControlHalVersion = mHwInterface.initOffloadControl(
// OffloadHardwareInterface guarantees that these callback
// methods are called on the handler passed to it, which is the
// same as mHandler, as coordinated by the setup in Tethering.
@@ -278,7 +280,7 @@
updateStatsForCurrentUpstream();
mUpstreamLinkProperties = null;
mHwInterface.stopOffloadControl();
- mControlInitialized = false;
+ mControlHalVersion = OFFLOAD_HAL_VERSION_NONE;
mConfigInitialized = false;
if (mHandler.hasCallbacks(mScheduledPollingTask)) {
mHandler.removeCallbacks(mScheduledPollingTask);
@@ -287,7 +289,7 @@
}
private boolean started() {
- return mConfigInitialized && mControlInitialized;
+ return mConfigInitialized && mControlHalVersion != OFFLOAD_HAL_VERSION_NONE;
}
@VisibleForTesting
@@ -696,6 +698,8 @@
}
final boolean isStarted = started();
pw.println("Offload HALs " + (isStarted ? "started" : "not started"));
+ pw.println("Offload Control HAL version: "
+ + OffloadHardwareInterface.halVerToString(mControlHalVersion));
LinkProperties lp = mUpstreamLinkProperties;
String upstream = (lp != null) ? lp.getInterfaceName() : null;
pw.println("Current upstream: " + upstream);
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index da5f25b..7685847 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -20,6 +20,7 @@
import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
import static android.net.util.TetheringUtils.uint16;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
@@ -38,12 +39,15 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
@@ -82,6 +86,37 @@
private final SharedLog mLog;
private final Dependencies mDeps;
private IOffloadControl mOffloadControl;
+
+ // TODO: Use major-minor version control to prevent from defining new constants.
+ static final int OFFLOAD_HAL_VERSION_NONE = 0;
+ static final int OFFLOAD_HAL_VERSION_1_0 = 1;
+ static final int OFFLOAD_HAL_VERSION_1_1 = 2;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "OFFLOAD_HAL_VERSION_", value = {
+ OFFLOAD_HAL_VERSION_NONE,
+ OFFLOAD_HAL_VERSION_1_0,
+ OFFLOAD_HAL_VERSION_1_1
+ })
+ public @interface OffloadHalVersion {}
+ @OffloadHalVersion
+ private int mOffloadControlVersion = OFFLOAD_HAL_VERSION_NONE;
+
+ @NonNull
+ static String halVerToString(int version) {
+ switch(version) {
+ case OFFLOAD_HAL_VERSION_1_0:
+ return "1.0";
+ case OFFLOAD_HAL_VERSION_1_1:
+ return "1.1";
+ case OFFLOAD_HAL_VERSION_NONE:
+ return "None";
+ default:
+ throw new IllegalArgumentException("Unsupported version int " + version);
+ }
+
+ }
+
private TetheringOffloadCallback mTetheringOffloadCallback;
private ControlCallback mControlCallback;
@@ -167,13 +202,30 @@
}
}
- public IOffloadControl getOffloadControl() {
+ @NonNull
+ public Pair<IOffloadControl, Integer> getOffloadControl() {
+ IOffloadControl hal = null;
+ int version = OFFLOAD_HAL_VERSION_NONE;
try {
- return IOffloadControl.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
- mLog.e("tethering offload control not supported: " + e);
- return null;
+ hal = android.hardware.tetheroffload.control
+ .V1_1.IOffloadControl.getService(true /*retry*/);
+ version = OFFLOAD_HAL_VERSION_1_1;
+ } catch (NoSuchElementException e) {
+ // Unsupported by device.
+ } catch (RemoteException e) {
+ mLog.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_1_1);
}
+ if (hal == null) {
+ try {
+ hal = IOffloadControl.getService(true /*retry*/);
+ version = OFFLOAD_HAL_VERSION_1_0;
+ } catch (NoSuchElementException e) {
+ // Unsupported by device.
+ } catch (RemoteException e) {
+ mLog.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_1_0);
+ }
+ }
+ return new Pair<IOffloadControl, Integer>(hal, version);
}
public NativeHandle createConntrackSocket(final int groups) {
@@ -304,16 +356,25 @@
}
}
- /** Initialize the tethering offload HAL. */
- public boolean initOffloadControl(ControlCallback controlCb) {
+ /**
+ * Initialize the tethering offload HAL.
+ *
+ * @return one of {@code OFFLOAD_HAL_VERSION_*} represents the HAL version, or
+ * {@link #OFFLOAD_HAL_VERSION_NONE} if failed.
+ */
+ public int initOffloadControl(ControlCallback controlCb) {
mControlCallback = controlCb;
if (mOffloadControl == null) {
- mOffloadControl = mDeps.getOffloadControl();
+ final Pair<IOffloadControl, Integer> halAndVersion = mDeps.getOffloadControl();
+ mOffloadControl = halAndVersion.first;
+ mOffloadControlVersion = halAndVersion.second;
if (mOffloadControl == null) {
mLog.e("tethering IOffloadControl.getService() returned null");
- return false;
+ return OFFLOAD_HAL_VERSION_NONE;
}
+ mLog.i("tethering offload control version "
+ + halVerToString(mOffloadControlVersion) + " is supported.");
}
final String logmsg = String.format("initOffloadControl(%s)",
@@ -331,11 +392,11 @@
});
} catch (RemoteException e) {
record(logmsg, e);
- return false;
+ return OFFLOAD_HAL_VERSION_NONE;
}
record(logmsg, results);
- return results.mSuccess;
+ return results.mSuccess ? mOffloadControlVersion : OFFLOAD_HAL_VERSION_NONE;
}
/** Stop IOffloadControl. */
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
index 9bd82f9..88f2054 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
@@ -29,6 +29,8 @@
import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE;
import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID;
import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
import static com.android.testutils.MiscAsserts.assertContainsAll;
import static com.android.testutils.MiscAsserts.assertThrows;
@@ -141,10 +143,10 @@
FakeSettingsProvider.clearSettingsProvider();
}
- private void setupFunctioningHardwareInterface() {
+ private void setupFunctioningHardwareInterface(int controlVersion) {
when(mHardware.initOffloadConfig()).thenReturn(true);
when(mHardware.initOffloadControl(mControlCallbackCaptor.capture()))
- .thenReturn(true);
+ .thenReturn(controlVersion);
when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true);
when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats());
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
@@ -170,6 +172,7 @@
ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
verify(mStatsManager).registerNetworkStatsProvider(anyString(),
tetherStatsProviderCaptor.capture());
+ reset(mStatsManager);
mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
assertNotNull(mTetherStatsProvider);
mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder();
@@ -177,10 +180,18 @@
return offload;
}
+ @Test
+ public void testStartStop() throws Exception {
+ stopOffloadController(
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/));
+ stopOffloadController(
+ startOffloadController(OFFLOAD_HAL_VERSION_1_1, true /*expectStart*/));
+ }
+
@NonNull
- private OffloadController startOffloadController(boolean expectStart)
+ private OffloadController startOffloadController(int controlVersion, boolean expectStart)
throws Exception {
- setupFunctioningHardwareInterface();
+ setupFunctioningHardwareInterface(controlVersion);
final OffloadController offload = makeOffloadController();
offload.start();
@@ -208,7 +219,7 @@
when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1);
assertThrows(SettingNotFoundException.class, () ->
Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED));
- startOffloadController(false /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, false /*expectStart*/);
}
@Test
@@ -216,26 +227,26 @@
when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0);
assertThrows(SettingNotFoundException.class, () ->
Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED));
- startOffloadController(true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
}
@Test
public void testSettingsAllowsStart() throws Exception {
Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
- startOffloadController(true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
}
@Test
public void testSettingsDisablesStart() throws Exception {
Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 1);
- startOffloadController(false /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, false /*expectStart*/);
}
@Test
public void testSetUpstreamLinkPropertiesWorking() throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
// In reality, the UpstreamNetworkMonitor would have passed down to us
// a covering set of local prefixes representing a minimum essential
@@ -406,7 +417,7 @@
public void testGetForwardedStats() throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
final String ethernetIface = "eth1";
final String mobileIface = "rmnet_data0";
@@ -496,7 +507,7 @@
public void testSetInterfaceQuota() throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
final String ethernetIface = "eth1";
final String mobileIface = "rmnet_data0";
@@ -558,7 +569,7 @@
public void testDataLimitCallback() throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedLimitReached();
@@ -569,7 +580,7 @@
public void testAddRemoveDownstreams() throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
final InOrder inOrder = inOrder(mHardware);
// Tethering makes several calls to setLocalPrefixes() before add/remove
@@ -636,7 +647,7 @@
public void testControlCallbackOnStoppedUnsupportedFetchesAllStats() throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
// Pretend to set a few different upstreams (only the interface name
// matters for this test; we're ignoring IP and route information).
@@ -667,7 +678,7 @@
throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
// Pretend to set a few different upstreams (only the interface name
// matters for this test; we're ignoring IP and route information).
@@ -745,7 +756,7 @@
enableOffload();
setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
final OffloadController offload =
- startOffloadController(true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
// Initialize with fake eth upstream.
final String ethernetIface = "eth1";
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
index 38b19dd..f4194e5 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -21,6 +21,8 @@
import static android.system.OsConstants.AF_UNIX;
import static android.system.OsConstants.SOCK_STREAM;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -45,6 +47,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import android.util.Pair;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -91,8 +94,8 @@
}
@Override
- public IOffloadControl getOffloadControl() {
- return mIOffloadControl;
+ public Pair<IOffloadControl, Integer> getOffloadControl() {
+ return new Pair<IOffloadControl, Integer>(mIOffloadControl, OFFLOAD_HAL_VERSION_1_0);
}
@Override
@@ -110,6 +113,7 @@
mControlCallback = spy(new OffloadHardwareInterface.ControlCallback());
}
+ // TODO: Pass version to test version specific operations.
private void startOffloadHardwareInterface() throws Exception {
mOffloadHw.initOffloadConfig();
mOffloadHw.initOffloadControl(mControlCallback);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 383fce1..d277e30 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -63,6 +63,8 @@
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
import static com.android.networkstack.tethering.TestConnectivityManager.BROADCAST_FIRST;
import static com.android.networkstack.tethering.TestConnectivityManager.CALLBACKS_FIRST;
import static com.android.networkstack.tethering.Tethering.UserRestrictionActionListener;
@@ -595,7 +597,7 @@
mInterfaceConfiguration.flags = new String[0];
when(mRouterAdvertisementDaemon.start())
.thenReturn(true);
- initOffloadConfiguration(true /* offloadConfig */, true /* offloadControl */,
+ initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_1_0,
0 /* defaultDisabled */);
when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats);
@@ -1866,7 +1868,7 @@
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
// 1. Offload fail if no OffloadConfig.
- initOffloadConfiguration(false /* offloadConfig */, true /* offloadControl */,
+ initOffloadConfiguration(false /* offloadConfig */, OFFLOAD_HAL_VERSION_1_0,
0 /* defaultDisabled */);
runUsbTethering(upstreamState);
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
@@ -1874,7 +1876,7 @@
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
reset(mUsbManager, mIPv6TetheringCoordinator);
// 2. Offload fail if no OffloadControl.
- initOffloadConfiguration(true /* offloadConfig */, false /* offloadControl */,
+ initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_NONE,
0 /* defaultDisabled */);
runUsbTethering(upstreamState);
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
@@ -1882,7 +1884,7 @@
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
reset(mUsbManager, mIPv6TetheringCoordinator);
// 3. Offload fail if disabled by settings.
- initOffloadConfiguration(true /* offloadConfig */, true /* offloadControl */,
+ initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_1_0,
1 /* defaultDisabled */);
runUsbTethering(upstreamState);
callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED);
@@ -1900,9 +1902,10 @@
}
private void initOffloadConfiguration(final boolean offloadConfig,
- final boolean offloadControl, final int defaultDisabled) {
+ @OffloadHardwareInterface.OffloadHalVersion final int offloadControlVersion,
+ final int defaultDisabled) {
when(mOffloadHardwareInterface.initOffloadConfig()).thenReturn(offloadConfig);
- when(mOffloadHardwareInterface.initOffloadControl(any())).thenReturn(offloadControl);
+ when(mOffloadHardwareInterface.initOffloadControl(any())).thenReturn(offloadControlVersion);
when(mOffloadHardwareInterface.getDefaultTetherOffloadDisabled()).thenReturn(
defaultDisabled);
}
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index b8f8aae..7e2f688 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -4713,6 +4713,22 @@
}
/**
+ * Temporarily allow bad wifi to override {@code config_networkAvoidBadWifi} configuration.
+ *
+ * @param timeMs The expired current time. The value should be set within a limited time from
+ * now.
+ *
+ * @hide
+ */
+ public void setTestAllowBadWifiUntil(long timeMs) {
+ try {
+ mService.setTestAllowBadWifiUntil(timeMs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Requests that the system open the captive portal app on the specified network.
*
* <p>This is to be used on networks where a captive portal was detected, as per
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index c434bbc..50ec781 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -226,4 +226,6 @@
void offerNetwork(int providerId, in NetworkScore score,
in NetworkCapabilities caps, in INetworkOfferCallback callback);
void unofferNetwork(in INetworkOfferCallback callback);
+
+ void setTestAllowBadWifiUntil(long timeMs);
}
diff --git a/framework/src/android/net/util/MultinetworkPolicyTracker.java b/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 0b42a00..7e62d28 100644
--- a/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -75,6 +75,7 @@
private volatile boolean mAvoidBadWifi = true;
private volatile int mMeteredMultipathPreference;
private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private volatile long mTestAllowBadWifiUntilMs = 0;
// Mainline module can't use internal HandlerExecutor, so add an identical executor here.
private static class HandlerExecutor implements Executor {
@@ -162,14 +163,31 @@
* Whether the device or carrier configuration disables avoiding bad wifi by default.
*/
public boolean configRestrictsAvoidBadWifi() {
+ final boolean allowBadWifi = mTestAllowBadWifiUntilMs > 0
+ && mTestAllowBadWifiUntilMs > System.currentTimeMillis();
+ // If the config returns true, then avoid bad wifi design can be controlled by the
+ // NETWORK_AVOID_BAD_WIFI setting.
+ if (allowBadWifi) return true;
+
// TODO: use R.integer.config_networkAvoidBadWifi directly
final int id = mResources.get().getIdentifier("config_networkAvoidBadWifi",
"integer", mResources.getResourcesContext().getPackageName());
return (getResourcesForActiveSubId().getInteger(id) == 0);
}
+ /**
+ * Temporarily allow bad wifi to override {@code config_networkAvoidBadWifi} configuration.
+ * The value works when the time set is more than {@link System.currentTimeMillis()}.
+ */
+ public void setTestAllowBadWifiUntil(long timeMs) {
+ Log.d(TAG, "setTestAllowBadWifiUntil: " + mTestAllowBadWifiUntilMs);
+ mTestAllowBadWifiUntilMs = timeMs;
+ updateAvoidBadWifi();
+ }
+
+ @VisibleForTesting
@NonNull
- private Resources getResourcesForActiveSubId() {
+ protected Resources getResourcesForActiveSubId() {
return SubscriptionManager.getResourcesForSubId(
mResources.getResourcesContext(), mActiveSubId);
}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index fd8397f..05bacfa 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -651,6 +651,12 @@
private static final int EVENT_MOBILE_DATA_PREFERRED_UIDS_CHANGED = 54;
/**
+ * Event to set temporary allow bad wifi within a limited time to override
+ * {@code config_networkAvoidBadWifi}.
+ */
+ private static final int EVENT_SET_TEST_ALLOW_BAD_WIFI_UNTIL = 55;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -662,6 +668,11 @@
*/
private static final int PROVISIONING_NOTIFICATION_HIDE = 0;
+ /**
+ * The maximum alive time to allow bad wifi configuration for testing.
+ */
+ private static final long MAX_TEST_ALLOW_BAD_WIFI_UNTIL_MS = 5 * 60 * 1000L;
+
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
}
@@ -4334,6 +4345,22 @@
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_AVOID_UNVALIDATED, network));
}
+ @Override
+ public void setTestAllowBadWifiUntil(long timeMs) {
+ enforceSettingsPermission();
+ if (!Build.isDebuggable()) {
+ throw new IllegalStateException("Does not support in non-debuggable build");
+ }
+
+ if (timeMs > System.currentTimeMillis() + MAX_TEST_ALLOW_BAD_WIFI_UNTIL_MS) {
+ throw new IllegalArgumentException("It should not exceed "
+ + MAX_TEST_ALLOW_BAD_WIFI_UNTIL_MS + "ms from now");
+ }
+
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_SET_TEST_ALLOW_BAD_WIFI_UNTIL, timeMs));
+ }
+
private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
if (DBG) log("handleSetAcceptUnvalidated network=" + network +
" accept=" + accept + " always=" + always);
@@ -4876,6 +4903,10 @@
case EVENT_MOBILE_DATA_PREFERRED_UIDS_CHANGED:
handleMobileDataPreferredUidsChanged();
break;
+ case EVENT_SET_TEST_ALLOW_BAD_WIFI_UNTIL:
+ final long timeMs = ((Long) msg.obj).longValue();
+ mMultinetworkPolicyTracker.setTestAllowBadWifiUntil(timeMs);
+ break;
}
}
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 8b57a92..35404bc 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -29,6 +29,8 @@
import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityManager.EXTRA_NETWORK;
+import static android.net.ConnectivityManager.EXTRA_NETWORK_REQUEST;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
@@ -74,12 +76,14 @@
import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
import static com.android.testutils.MiscAsserts.assertThrows;
import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
import static com.android.testutils.TestPermissionUtil.runAsShell;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -205,6 +209,7 @@
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -847,6 +852,119 @@
}
}
+ private void runIdenticalPendingIntentsRequestTest(boolean useListen) throws Exception {
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
+
+ // Disconnect before registering callbacks, reconnect later to fire them
+ mCtsNetUtils.ensureWifiDisconnected(null);
+
+ final NetworkRequest firstRequest = makeWifiNetworkRequest();
+ final NetworkRequest secondRequest = new NetworkRequest(firstRequest);
+ // Will match wifi or test, since transports are ORed; but there should only be wifi
+ secondRequest.networkCapabilities.addTransportType(TRANSPORT_TEST);
+
+ PendingIntent firstIntent = null;
+ PendingIntent secondIntent = null;
+ BroadcastReceiver receiver = null;
+
+ // Avoid receiving broadcasts from other runs by appending a timestamp
+ final String broadcastAction = NETWORK_CALLBACK_ACTION + System.currentTimeMillis();
+ try {
+ // TODO: replace with PendingIntent.FLAG_MUTABLE when this code compiles against S+
+ // Intent is mutable to receive EXTRA_NETWORK_REQUEST from ConnectivityService
+ final int pendingIntentFlagMutable = 1 << 25;
+ final String extraBoolKey = "extra_bool";
+ firstIntent = PendingIntent.getBroadcast(mContext,
+ 0 /* requestCode */,
+ new Intent(broadcastAction).putExtra(extraBoolKey, false),
+ PendingIntent.FLAG_UPDATE_CURRENT | pendingIntentFlagMutable);
+
+ if (useListen) {
+ mCm.registerNetworkCallback(firstRequest, firstIntent);
+ } else {
+ mCm.requestNetwork(firstRequest, firstIntent);
+ }
+
+ // Second intent equals the first as per filterEquals (extras don't count), so first
+ // intent will be updated with the new extras
+ secondIntent = PendingIntent.getBroadcast(mContext,
+ 0 /* requestCode */,
+ new Intent(broadcastAction).putExtra(extraBoolKey, true),
+ PendingIntent.FLAG_UPDATE_CURRENT | pendingIntentFlagMutable);
+
+ // Because secondIntent.intentFilterEquals the first, the request should be replaced
+ if (useListen) {
+ mCm.registerNetworkCallback(secondRequest, secondIntent);
+ } else {
+ mCm.requestNetwork(secondRequest, secondIntent);
+ }
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(broadcastAction);
+
+ final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
+ final AtomicInteger receivedCount = new AtomicInteger(0);
+ receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final NetworkRequest request = intent.getParcelableExtra(EXTRA_NETWORK_REQUEST);
+ assertPendingIntentRequestMatches(request, secondRequest, useListen);
+ receivedCount.incrementAndGet();
+ networkFuture.complete(intent.getParcelableExtra(EXTRA_NETWORK));
+ }
+ };
+ mContext.registerReceiver(receiver, filter);
+
+ final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+ try {
+ assertEquals(wifiNetwork, networkFuture.get(
+ NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (TimeoutException e) {
+ throw new AssertionError("PendingIntent not received for " + secondRequest, e);
+ }
+
+ // Sleep for a small amount of time to try to check that only one callback is ever
+ // received (so the first callback was really unregistered). This does not guarantee
+ // that the test will fail if it runs very slowly, but it should at least be very
+ // noticeably flaky.
+ Thread.sleep(NO_CALLBACK_TIMEOUT_MS);
+
+ // TODO: BUG (b/189868426): this should also apply to listens
+ if (!useListen) {
+ assertEquals("PendingIntent should only be received once", 1, receivedCount.get());
+ }
+ } finally {
+ if (firstIntent != null) mCm.unregisterNetworkCallback(firstIntent);
+ if (secondIntent != null) mCm.unregisterNetworkCallback(secondIntent);
+ if (receiver != null) mContext.unregisterReceiver(receiver);
+ mCtsNetUtils.ensureWifiConnected();
+ }
+ }
+
+ private void assertPendingIntentRequestMatches(NetworkRequest broadcasted, NetworkRequest filed,
+ boolean useListen) {
+ // TODO: BUG (b/191713869): on S the request extra is null on listens
+ if (isAtLeastS() && useListen && broadcasted == null) return;
+ assertArrayEquals(filed.networkCapabilities.getCapabilities(),
+ broadcasted.networkCapabilities.getCapabilities());
+ // TODO: BUG (b/189868426): this should also apply to listens
+ if (useListen) return;
+ assertArrayEquals(filed.networkCapabilities.getTransportTypes(),
+ broadcasted.networkCapabilities.getTransportTypes());
+ }
+
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
+ public void testRegisterNetworkRequest_identicalPendingIntents() throws Exception {
+ runIdenticalPendingIntentsRequestTest(false /* useListen */);
+ }
+
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
+ public void testRegisterNetworkCallback_identicalPendingIntents() throws Exception {
+ runIdenticalPendingIntentsRequestTest(true /* useListen */);
+ }
+
/**
* Exercises the requestNetwork with NetworkCallback API. This checks to
* see if we get a callback for an INTERNET request.
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
index ae38faa..a9a3380 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
@@ -43,6 +43,7 @@
import android.net.ConnectivityManager;
import android.net.IpSecAlgorithm;
import android.net.IpSecManager;
+import android.net.IpSecManager.IpSecTunnelInterface;
import android.net.IpSecTransform;
import android.net.LinkAddress;
import android.net.Network;
@@ -50,25 +51,33 @@
import android.net.TestNetworkManager;
import android.net.cts.PacketUtils.Payload;
import android.net.cts.util.CtsNetUtils;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.AppModeFull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+
@RunWith(AndroidJUnit4.class)
@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
public class IpSecManagerTunnelTest extends IpSecBaseTest {
+ @Rule public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
private static final String TAG = IpSecManagerTunnelTest.class.getSimpleName();
private static final InetAddress LOCAL_OUTER_4 = InetAddress.parseNumericAddress("192.0.2.1");
@@ -78,6 +87,15 @@
private static final InetAddress REMOTE_OUTER_6 =
InetAddress.parseNumericAddress("2001:db8:1::2");
+ private static final InetAddress LOCAL_OUTER_4_NEW =
+ InetAddress.parseNumericAddress("192.0.2.101");
+ private static final InetAddress REMOTE_OUTER_4_NEW =
+ InetAddress.parseNumericAddress("192.0.2.102");
+ private static final InetAddress LOCAL_OUTER_6_NEW =
+ InetAddress.parseNumericAddress("2001:db8:1::101");
+ private static final InetAddress REMOTE_OUTER_6_NEW =
+ InetAddress.parseNumericAddress("2001:db8:1::102");
+
private static final InetAddress LOCAL_INNER_4 =
InetAddress.parseNumericAddress("198.51.100.1");
private static final InetAddress REMOTE_INNER_4 =
@@ -95,10 +113,9 @@
// Static state to reduce setup/teardown
private static ConnectivityManager sCM;
private static TestNetworkManager sTNM;
- private static ParcelFileDescriptor sTunFd;
- private static TestNetworkCallback sTunNetworkCallback;
- private static Network sTunNetwork;
- private static TunUtils sTunUtils;
+
+ private static TunNetworkWrapper sTunWrapper;
+ private static TunNetworkWrapper sTunWrapperNew;
private static Context sContext = InstrumentationRegistry.getContext();
private static final CtsNetUtils mCtsNetUtils = new CtsNetUtils(sContext);
@@ -116,19 +133,8 @@
// right appop permissions.
mCtsNetUtils.setAppopPrivileged(OP_MANAGE_IPSEC_TUNNELS, true);
- TestNetworkInterface testIface =
- sTNM.createTunInterface(
- new LinkAddress[] {
- new LinkAddress(LOCAL_OUTER_4, IP4_PREFIX_LEN),
- new LinkAddress(LOCAL_OUTER_6, IP6_PREFIX_LEN)
- });
-
- sTunFd = testIface.getFileDescriptor();
- sTunNetworkCallback = mCtsNetUtils.setupAndGetTestNetwork(testIface.getInterfaceName());
- sTunNetworkCallback.waitForAvailable();
- sTunNetwork = sTunNetworkCallback.currentNetwork;
-
- sTunUtils = new TunUtils(sTunFd);
+ sTunWrapper = new TunNetworkWrapper(LOCAL_OUTER_4, LOCAL_OUTER_6);
+ sTunWrapperNew = new TunNetworkWrapper(LOCAL_OUTER_4_NEW, LOCAL_OUTER_6_NEW);
}
@Before
@@ -139,24 +145,76 @@
// Set to true before every run; some tests flip this.
mCtsNetUtils.setAppopPrivileged(OP_MANAGE_IPSEC_TUNNELS, true);
- // Clear sTunUtils state
- sTunUtils.reset();
+ // Clear TunUtils state
+ sTunWrapper.utils.reset();
+ sTunWrapperNew.utils.reset();
+ }
+
+ private static void tearDownTunWrapperIfNotNull(TunNetworkWrapper tunWrapper) throws Exception {
+ if (tunWrapper != null) {
+ tunWrapper.tearDown();
+ }
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
mCtsNetUtils.setAppopPrivileged(OP_MANAGE_IPSEC_TUNNELS, false);
- sCM.unregisterNetworkCallback(sTunNetworkCallback);
-
- sTNM.teardownTestNetwork(sTunNetwork);
- sTunFd.close();
+ tearDownTunWrapperIfNotNull(sTunWrapper);
+ tearDownTunWrapperIfNotNull(sTunWrapperNew);
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.dropShellPermissionIdentity();
}
+ private static class TunNetworkWrapper {
+ public final ParcelFileDescriptor fd;
+ public final TestNetworkCallback networkCallback;
+ public final Network network;
+ public final TunUtils utils;
+
+ TunNetworkWrapper(InetAddress... addresses) throws Exception {
+ final LinkAddress[] linkAddresses = new LinkAddress[addresses.length];
+ for (int i = 0; i < linkAddresses.length; i++) {
+ InetAddress addr = addresses[i];
+ if (addr instanceof Inet4Address) {
+ linkAddresses[i] = new LinkAddress(addr, IP4_PREFIX_LEN);
+ } else {
+ linkAddresses[i] = new LinkAddress(addr, IP6_PREFIX_LEN);
+ }
+ }
+
+ try {
+ final TestNetworkInterface testIface = sTNM.createTunInterface(linkAddresses);
+
+ fd = testIface.getFileDescriptor();
+ networkCallback = mCtsNetUtils.setupAndGetTestNetwork(testIface.getInterfaceName());
+ networkCallback.waitForAvailable();
+ network = networkCallback.currentNetwork;
+ } catch (Exception e) {
+ tearDown();
+ throw e;
+ }
+
+ utils = new TunUtils(fd);
+ }
+
+ public void tearDown() throws Exception {
+ if (networkCallback != null) {
+ sCM.unregisterNetworkCallback(networkCallback);
+ }
+
+ if (network != null) {
+ sTNM.teardownTestNetwork(network);
+ }
+
+ if (fd != null) {
+ fd.close();
+ }
+ }
+ }
+
@Test
public void testSecurityExceptionCreateTunnelInterfaceWithoutAppop() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -166,7 +224,7 @@
// Security exceptions are thrown regardless of IPv4/IPv6. Just test one
try {
- mISM.createIpSecTunnelInterface(LOCAL_INNER_6, REMOTE_INNER_6, sTunNetwork);
+ mISM.createIpSecTunnelInterface(LOCAL_INNER_6, REMOTE_INNER_6, sTunWrapper.network);
fail("Did not throw SecurityException for Tunnel creation without appop");
} catch (SecurityException expected) {
}
@@ -196,11 +254,16 @@
* Runs the test code, and returns the inner socket port, if any.
*
* @param ipsecNetwork The IPsec Interface based Network for binding sockets on
+ * @param tunnelIface The IPsec tunnel interface that will be tested
+ * @param underlyingTunUtils The utility of the IPsec tunnel interface's underlying TUN
+ * network
* @return the integer port of the inner socket if outbound, or 0 if inbound
* IpSecTunnelTestRunnable
* @throws Exception if any part of the test failed.
*/
- public abstract int run(Network ipsecNetwork) throws Exception;
+ public abstract int run(
+ Network ipsecNetwork, IpSecTunnelInterface tunnelIface, TunUtils underlyingTunUtils)
+ throws Exception;
}
private int getPacketSize(
@@ -265,7 +328,9 @@
int expectedPacketSize) {
return new IpSecTunnelTestRunnable() {
@Override
- public int run(Network ipsecNetwork) throws Exception {
+ public int run(
+ Network ipsecNetwork, IpSecTunnelInterface tunnelIface, TunUtils tunUtils)
+ throws Exception {
// Build a socket and send traffic
JavaUdpSocket socket = new JavaUdpSocket(localInner);
ipsecNetwork.bindSocket(socket.mSocket);
@@ -284,7 +349,7 @@
// Verify that an encrypted packet is sent. As of right now, checking encrypted
// body is not possible, due to the test not knowing some of the fields of the
// inner IP header (flow label, flags, etc)
- sTunUtils.awaitEspPacketNoPlaintext(
+ tunUtils.awaitEspPacketNoPlaintext(
spi, TEST_DATA, encapPort != 0, expectedPacketSize);
socket.close();
@@ -312,7 +377,9 @@
throws Exception {
return new IpSecTunnelTestRunnable() {
@Override
- public int run(Network ipsecNetwork) throws Exception {
+ public int run(
+ Network ipsecNetwork, IpSecTunnelInterface tunnelIface, TunUtils tunUtils)
+ throws Exception {
// Build a socket and receive traffic
JavaUdpSocket socket = new JavaUdpSocket(localInner, innerSocketPort);
ipsecNetwork.bindSocket(socket.mSocket);
@@ -325,7 +392,7 @@
socket.mSocket, IpSecManager.DIRECTION_OUT, inTransportTransform);
}
- sTunUtils.reflectPackets();
+ tunUtils.reflectPackets();
// Receive packet from socket, and validate that the payload is correct
receiveAndValidatePacket(socket);
@@ -355,7 +422,9 @@
throws Exception {
return new IpSecTunnelTestRunnable() {
@Override
- public int run(Network ipsecNetwork) throws Exception {
+ public int run(
+ Network ipsecNetwork, IpSecTunnelInterface tunnelIface, TunUtils tunUtils)
+ throws Exception {
// Build a socket and receive traffic
JavaUdpSocket socket = new JavaUdpSocket(localInner);
ipsecNetwork.bindSocket(socket.mSocket);
@@ -391,7 +460,7 @@
socket.getPort(),
encapPort);
}
- sTunUtils.injectPacket(pkt);
+ tunUtils.injectPacket(pkt);
// Receive packet from socket, and validate
receiveAndValidatePacket(socket);
@@ -404,6 +473,161 @@
}
}
+ private class MigrateIpSecTunnelTestRunnableFactory implements IpSecTunnelTestRunnableFactory {
+ private final IpSecTunnelTestRunnableFactory mTestRunnableFactory;
+
+ MigrateIpSecTunnelTestRunnableFactory(boolean isOutputTest) {
+ if (isOutputTest) {
+ mTestRunnableFactory = new OutputIpSecTunnelTestRunnableFactory();
+ } else {
+ mTestRunnableFactory = new InputPacketGeneratorIpSecTunnelTestRunnableFactory();
+ }
+ }
+
+ @Override
+ public IpSecTunnelTestRunnable getIpSecTunnelTestRunnable(
+ boolean transportInTunnelMode,
+ int spi,
+ InetAddress localInner,
+ InetAddress remoteInner,
+ InetAddress localOuter,
+ InetAddress remoteOuter,
+ IpSecTransform inTransportTransform,
+ IpSecTransform outTransportTransform,
+ int encapPort,
+ int unusedInnerSocketPort,
+ int expectedPacketSize) {
+ return new IpSecTunnelTestRunnable() {
+ @Override
+ public int run(
+ Network ipsecNetwork, IpSecTunnelInterface tunnelIface, TunUtils tunUtils)
+ throws Exception {
+ mTestRunnableFactory
+ .getIpSecTunnelTestRunnable(
+ transportInTunnelMode,
+ spi,
+ localInner,
+ remoteInner,
+ localOuter,
+ remoteOuter,
+ inTransportTransform,
+ outTransportTransform,
+ encapPort,
+ unusedInnerSocketPort,
+ expectedPacketSize)
+ .run(ipsecNetwork, tunnelIface, sTunWrapper.utils);
+
+ tunnelIface.setUnderlyingNetwork(sTunWrapperNew.network);
+
+ // Verify migrating to IPv4 and IPv6 addresses. It ensures that not only
+ // can IPsec tunnel migrate across interfaces, IPsec tunnel can also migrate to
+ // a different address on the same interface.
+ checkMigratedTunnel(
+ localInner,
+ remoteInner,
+ LOCAL_OUTER_4_NEW,
+ REMOTE_OUTER_4_NEW,
+ encapPort != 0,
+ transportInTunnelMode,
+ sTunWrapperNew.utils,
+ tunnelIface,
+ ipsecNetwork);
+ checkMigratedTunnel(
+ localInner,
+ remoteInner,
+ LOCAL_OUTER_6_NEW,
+ REMOTE_OUTER_6_NEW,
+ false, // IPv6 does not support UDP encapsulation
+ transportInTunnelMode,
+ sTunWrapperNew.utils,
+ tunnelIface,
+ ipsecNetwork);
+
+ return 0;
+ }
+ };
+ }
+
+ private void checkMigratedTunnel(
+ InetAddress localInner,
+ InetAddress remoteInner,
+ InetAddress localOuter,
+ InetAddress remoteOuter,
+ boolean useEncap,
+ boolean transportInTunnelMode,
+ TunUtils tunUtils,
+ IpSecTunnelInterface tunnelIface,
+ Network ipsecNetwork)
+ throws Exception {
+
+ // Preselect both SPI and encap port, to be used for both inbound and outbound tunnels.
+ // Re-uses the same SPI to ensure that even in cases of symmetric SPIs shared across
+ // tunnel and transport mode, packets are encrypted/decrypted properly based on the
+ // src/dst.
+ int spi = getRandomSpi(localOuter, remoteOuter);
+
+ int innerFamily = localInner instanceof Inet4Address ? AF_INET : AF_INET6;
+ int outerFamily = localOuter instanceof Inet4Address ? AF_INET : AF_INET6;
+ int expectedPacketSize =
+ getPacketSize(innerFamily, outerFamily, useEncap, transportInTunnelMode);
+
+ // Build transport mode transforms and encapsulation socket for verifying
+ // transport-in-tunnel case and encapsulation case.
+ try (IpSecManager.SecurityParameterIndex inTransportSpi =
+ mISM.allocateSecurityParameterIndex(localInner, spi);
+ IpSecManager.SecurityParameterIndex outTransportSpi =
+ mISM.allocateSecurityParameterIndex(remoteInner, spi);
+ IpSecTransform inTransportTransform =
+ buildIpSecTransform(sContext, inTransportSpi, null, remoteInner);
+ IpSecTransform outTransportTransform =
+ buildIpSecTransform(sContext, outTransportSpi, null, localInner);
+ UdpEncapsulationSocket encapSocket = mISM.openUdpEncapsulationSocket()) {
+
+ // Configure tunnel mode Transform parameters
+ IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(sContext);
+ transformBuilder.setEncryption(
+ new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
+ transformBuilder.setAuthentication(
+ new IpSecAlgorithm(
+ IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4));
+
+ if (useEncap) {
+ transformBuilder.setIpv4Encapsulation(encapSocket, encapSocket.getPort());
+ }
+
+ // Apply transform and check that traffic is properly encrypted
+ try (IpSecManager.SecurityParameterIndex inSpi =
+ mISM.allocateSecurityParameterIndex(localOuter, spi);
+ IpSecManager.SecurityParameterIndex outSpi =
+ mISM.allocateSecurityParameterIndex(remoteOuter, spi);
+ IpSecTransform inTransform =
+ transformBuilder.buildTunnelModeTransform(remoteOuter, inSpi);
+ IpSecTransform outTransform =
+ transformBuilder.buildTunnelModeTransform(localOuter, outSpi)) {
+ mISM.applyTunnelModeTransform(
+ tunnelIface, IpSecManager.DIRECTION_IN, inTransform);
+ mISM.applyTunnelModeTransform(
+ tunnelIface, IpSecManager.DIRECTION_OUT, outTransform);
+
+ mTestRunnableFactory
+ .getIpSecTunnelTestRunnable(
+ transportInTunnelMode,
+ spi,
+ localInner,
+ remoteInner,
+ localOuter,
+ remoteOuter,
+ inTransportTransform,
+ outTransportTransform,
+ useEncap ? encapSocket.getPort() : 0,
+ 0,
+ expectedPacketSize)
+ .run(ipsecNetwork, tunnelIface, tunUtils);
+ }
+ }
+ }
+ }
+
private void checkTunnelOutput(
int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
throws Exception {
@@ -426,6 +650,28 @@
new InputPacketGeneratorIpSecTunnelTestRunnableFactory());
}
+ private void checkMigrateTunnelOutput(
+ int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
+ throws Exception {
+ checkTunnel(
+ innerFamily,
+ outerFamily,
+ useEncap,
+ transportInTunnelMode,
+ new MigrateIpSecTunnelTestRunnableFactory(true));
+ }
+
+ private void checkMigrateTunnelInput(
+ int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
+ throws Exception {
+ checkTunnel(
+ innerFamily,
+ outerFamily,
+ useEncap,
+ transportInTunnelMode,
+ new MigrateIpSecTunnelTestRunnableFactory(false));
+ }
+
/**
* Validates that the kernel can talk to itself.
*
@@ -579,7 +825,8 @@
IpSecManager.SecurityParameterIndex outSpi =
mISM.allocateSecurityParameterIndex(remoteOuter, spi);
IpSecManager.IpSecTunnelInterface tunnelIface =
- mISM.createIpSecTunnelInterface(localOuter, remoteOuter, sTunNetwork)) {
+ mISM.createIpSecTunnelInterface(
+ localOuter, remoteOuter, sTunWrapper.network)) {
// Build the test network
tunnelIface.addAddress(localInner, innerPrefixLen);
testNetworkCb = mCtsNetUtils.setupAndGetTestNetwork(tunnelIface.getInterfaceName());
@@ -615,7 +862,7 @@
mISM.applyTunnelModeTransform(
tunnelIface, IpSecManager.DIRECTION_OUT, outTransform);
- innerSocketPort = test.run(testNetwork);
+ innerSocketPort = test.run(testNetwork, tunnelIface, sTunWrapper.utils);
}
// Teardown the test network
@@ -739,6 +986,14 @@
return maybeEncapPacket(srcOuter, dstOuter, encapPort, espPayload).getPacketBytes();
}
+ private void doTestMigrateTunnel(
+ int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
+ throws Exception {
+ assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
+ checkTunnelOutput(innerFamily, outerFamily, useEncap, transportInTunnelMode);
+ checkTunnelInput(innerFamily, outerFamily, useEncap, transportInTunnelMode);
+ }
+
// Transport-in-Tunnel mode tests
@Test
public void testTransportInTunnelModeV4InV4() throws Exception {
@@ -747,6 +1002,12 @@
checkTunnelInput(AF_INET, AF_INET, false, true);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTransportInTunnelModeV4InV4() throws Exception {
+ doTestMigrateTunnel(AF_INET, AF_INET, false, true);
+ }
+
@Test
public void testTransportInTunnelModeV4InV4Reflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -760,6 +1021,12 @@
checkTunnelInput(AF_INET, AF_INET, true, true);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTransportInTunnelModeV4InV4UdpEncap() throws Exception {
+ doTestMigrateTunnel(AF_INET, AF_INET, true, true);
+ }
+
@Test
public void testTransportInTunnelModeV4InV4UdpEncapReflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -773,6 +1040,12 @@
checkTunnelInput(AF_INET, AF_INET6, false, true);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTransportInTunnelModeV4InV6() throws Exception {
+ doTestMigrateTunnel(AF_INET, AF_INET6, false, true);
+ }
+
@Test
public void testTransportInTunnelModeV4InV6Reflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -786,6 +1059,12 @@
checkTunnelInput(AF_INET6, AF_INET, false, true);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTransportInTunnelModeV6InV4() throws Exception {
+ doTestMigrateTunnel(AF_INET6, AF_INET, false, true);
+ }
+
@Test
public void testTransportInTunnelModeV6InV4Reflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -799,6 +1078,12 @@
checkTunnelInput(AF_INET6, AF_INET, true, true);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTransportInTunnelModeV6InV4UdpEncap() throws Exception {
+ doTestMigrateTunnel(AF_INET6, AF_INET, true, true);
+ }
+
@Test
public void testTransportInTunnelModeV6InV4UdpEncapReflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -812,6 +1097,12 @@
checkTunnelInput(AF_INET, AF_INET6, false, true);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTransportInTunnelModeV6InV6() throws Exception {
+ doTestMigrateTunnel(AF_INET, AF_INET6, false, true);
+ }
+
@Test
public void testTransportInTunnelModeV6InV6Reflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -826,6 +1117,12 @@
checkTunnelInput(AF_INET, AF_INET, false, false);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTunnelV4InV4() throws Exception {
+ doTestMigrateTunnel(AF_INET, AF_INET, false, false);
+ }
+
@Test
public void testTunnelV4InV4Reflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -839,6 +1136,12 @@
checkTunnelInput(AF_INET, AF_INET, true, false);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTunnelV4InV4UdpEncap() throws Exception {
+ doTestMigrateTunnel(AF_INET, AF_INET, true, false);
+ }
+
@Test
public void testTunnelV4InV4UdpEncapReflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -852,6 +1155,12 @@
checkTunnelInput(AF_INET, AF_INET6, false, false);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTunnelV4InV6() throws Exception {
+ doTestMigrateTunnel(AF_INET, AF_INET6, false, false);
+ }
+
@Test
public void testTunnelV4InV6Reflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -865,6 +1174,12 @@
checkTunnelInput(AF_INET6, AF_INET, false, false);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTunnelV6InV4() throws Exception {
+ doTestMigrateTunnel(AF_INET6, AF_INET, false, false);
+ }
+
@Test
public void testTunnelV6InV4Reflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -878,6 +1193,12 @@
checkTunnelInput(AF_INET6, AF_INET, true, false);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTunnelV6InV4UdpEncap() throws Exception {
+ doTestMigrateTunnel(AF_INET6, AF_INET, true, false);
+ }
+
@Test
public void testTunnelV6InV4UdpEncapReflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
@@ -891,6 +1212,12 @@
checkTunnelInput(AF_INET6, AF_INET6, false, false);
}
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
+ public void testMigrateTunnelV6InV6() throws Exception {
+ doTestMigrateTunnel(AF_INET6, AF_INET6, false, false);
+ }
+
@Test
public void testTunnelV6InV6Reflected() throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 3b030d6..6c484cc 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -1496,8 +1496,7 @@
return mService.getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
}
- private static class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
- volatile boolean mConfigRestrictsAvoidBadWifi;
+ private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
volatile int mConfigMeteredMultipathPreference;
WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
@@ -1505,8 +1504,8 @@
}
@Override
- public boolean configRestrictsAvoidBadWifi() {
- return mConfigRestrictsAvoidBadWifi;
+ protected Resources getResourcesForActiveSubId() {
+ return mResources;
}
@Override
@@ -1723,7 +1722,9 @@
.getIdentifier(eq("config_networkSupportedKeepaliveCount"), eq("array"), any());
doReturn(R.array.network_switch_type_name).when(mResources)
.getIdentifier(eq("network_switch_type_name"), eq("array"), any());
-
+ doReturn(R.integer.config_networkAvoidBadWifi).when(mResources)
+ .getIdentifier(eq("config_networkAvoidBadWifi"), eq("integer"), any());
+ doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
final ConnectivityResources connRes = mock(ConnectivityResources.class);
doReturn(mResources).when(connRes).get();
@@ -4645,30 +4646,29 @@
}
@Test
- public void testAvoidBadWifiSetting() throws Exception {
+ public void testSetAllowBadWifiUntil() throws Exception {
+ runAsShell(NETWORK_SETTINGS,
+ () -> mService.setTestAllowBadWifiUntil(System.currentTimeMillis() + 5_000L));
+ waitForIdle();
+ testAvoidBadWifiConfig_controlledBySettings();
+
+ runAsShell(NETWORK_SETTINGS,
+ () -> mService.setTestAllowBadWifiUntil(System.currentTimeMillis() - 5_000L));
+ waitForIdle();
+ testAvoidBadWifiConfig_ignoreSettings();
+ }
+
+ private void testAvoidBadWifiConfig_controlledBySettings() {
final ContentResolver cr = mServiceContext.getContentResolver();
final String settingName = ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
- mPolicyTracker.mConfigRestrictsAvoidBadWifi = false;
- String[] values = new String[] {null, "0", "1"};
- for (int i = 0; i < values.length; i++) {
- Settings.Global.putInt(cr, settingName, 1);
- mPolicyTracker.reevaluate();
- waitForIdle();
- String msg = String.format("config=false, setting=%s", values[i]);
- assertTrue(mService.avoidBadWifi());
- assertFalse(msg, mPolicyTracker.shouldNotifyWifiUnvalidated());
- }
-
- mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
-
- Settings.Global.putInt(cr, settingName, 0);
+ Settings.Global.putString(cr, settingName, "0");
mPolicyTracker.reevaluate();
waitForIdle();
assertFalse(mService.avoidBadWifi());
assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated());
- Settings.Global.putInt(cr, settingName, 1);
+ Settings.Global.putString(cr, settingName, "1");
mPolicyTracker.reevaluate();
waitForIdle();
assertTrue(mService.avoidBadWifi());
@@ -4681,13 +4681,40 @@
assertTrue(mPolicyTracker.shouldNotifyWifiUnvalidated());
}
+ private void testAvoidBadWifiConfig_ignoreSettings() {
+ final ContentResolver cr = mServiceContext.getContentResolver();
+ final String settingName = ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
+
+ String[] values = new String[] {null, "0", "1"};
+ for (int i = 0; i < values.length; i++) {
+ Settings.Global.putString(cr, settingName, values[i]);
+ mPolicyTracker.reevaluate();
+ waitForIdle();
+ String msg = String.format("config=false, setting=%s", values[i]);
+ assertTrue(mService.avoidBadWifi());
+ assertFalse(msg, mPolicyTracker.shouldNotifyWifiUnvalidated());
+ }
+ }
+
+ @Test
+ public void testAvoidBadWifiSetting() throws Exception {
+ final ContentResolver cr = mServiceContext.getContentResolver();
+ final String settingName = ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
+
+ doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+ testAvoidBadWifiConfig_ignoreSettings();
+
+ doReturn(0).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+ testAvoidBadWifiConfig_controlledBySettings();
+ }
+
@Ignore("Refactoring in progress b/178071397")
@Test
public void testAvoidBadWifi() throws Exception {
final ContentResolver cr = mServiceContext.getContentResolver();
// Pretend we're on a carrier that restricts switching away from bad wifi.
- mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
+ doReturn(0).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
// File a request for cell to ensure it doesn't go down.
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
@@ -4738,13 +4765,13 @@
// Simulate switching to a carrier that does not restrict avoiding bad wifi, and expect
// that we switch back to cell.
- mPolicyTracker.mConfigRestrictsAvoidBadWifi = false;
+ doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
mPolicyTracker.reevaluate();
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// Switch back to a restrictive carrier.
- mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
+ doReturn(0).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
mPolicyTracker.reevaluate();
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);