Merge "[DK4] Add cts to test automatic on/off keepalive"
This commit is contained in:
commit
4bc39e2d22
3 changed files with 215 additions and 0 deletions
|
|
@ -8,6 +8,9 @@
|
|||
},
|
||||
{
|
||||
"exclude-annotation": "android.platform.test.annotations.FlakyTest"
|
||||
},
|
||||
{
|
||||
"exclude-annotation": "android.platform.test.annotations.RequiresDevice"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package com.android.cts.net.hostside;
|
|||
|
||||
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
|
||||
import static android.Manifest.permission.NETWORK_SETTINGS;
|
||||
import static android.Manifest.permission.READ_DEVICE_CONFIG;
|
||||
import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
|
||||
import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
|
||||
import static android.content.pm.PackageManager.FEATURE_WIFI;
|
||||
import static android.net.ConnectivityManager.TYPE_VPN;
|
||||
|
|
@ -36,6 +38,12 @@ import static android.test.MoreAsserts.assertNotEqual;
|
|||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
|
||||
import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_DATA_RECEIVED;
|
||||
import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_ERROR;
|
||||
import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_PAUSED;
|
||||
import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_RESUMED;
|
||||
import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_STARTED;
|
||||
import static com.android.cts.net.hostside.VpnTest.TestSocketKeepaliveCallback.CallbackType.ON_STOPPED;
|
||||
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.networkstack.apishim.ConstantsShim.RECEIVER_EXPORTED;
|
||||
|
|
@ -64,6 +72,7 @@ import android.content.pm.PackageManager;
|
|||
import android.database.Cursor;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.ConnectivityManager.NetworkCallback;
|
||||
import android.net.IpSecManager;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
|
|
@ -71,6 +80,7 @@ import android.net.NetworkCapabilities;
|
|||
import android.net.NetworkRequest;
|
||||
import android.net.Proxy;
|
||||
import android.net.ProxyInfo;
|
||||
import android.net.SocketKeepalive;
|
||||
import android.net.TestNetworkInterface;
|
||||
import android.net.TestNetworkManager;
|
||||
import android.net.TransportInfo;
|
||||
|
|
@ -79,6 +89,7 @@ import android.net.VpnManager;
|
|||
import android.net.VpnService;
|
||||
import android.net.VpnTransportInfo;
|
||||
import android.net.cts.util.CtsNetUtils;
|
||||
import android.net.util.KeepaliveUtils;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
|
|
@ -87,6 +98,7 @@ import android.os.ParcelFileDescriptor;
|
|||
import android.os.Process;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.Settings;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.support.test.uiautomator.UiObject;
|
||||
|
|
@ -105,6 +117,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|||
|
||||
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
|
||||
import com.android.modules.utils.build.SdkLevel;
|
||||
import com.android.net.module.util.ArrayTrackRecord;
|
||||
import com.android.net.module.util.CollectionUtils;
|
||||
import com.android.net.module.util.PacketBuilder;
|
||||
import com.android.testutils.DevSdkIgnoreRule;
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||
|
|
@ -141,6 +155,7 @@ import java.util.Random;
|
|||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
|
|
@ -190,6 +205,12 @@ public class VpnTest {
|
|||
public static int SOCKET_TIMEOUT_MS = 100;
|
||||
public static String TEST_HOST = "connectivitycheck.gstatic.com";
|
||||
|
||||
private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION =
|
||||
"automatic_on_off_keepalive_version";
|
||||
// Enabled since version 1 means it's always enabled because the version is always above 1
|
||||
private static final String AUTOMATIC_ON_OFF_KEEPALIVE_ENABLED = "1";
|
||||
private static final long TEST_TCP_POLLING_TIMER_EXPIRED_PERIOD_MS = 60_000L;
|
||||
|
||||
private UiDevice mDevice;
|
||||
private MyActivity mActivity;
|
||||
private String mPackageName;
|
||||
|
|
@ -1041,6 +1062,183 @@ public class VpnTest {
|
|||
checkStrictModePrivateDns();
|
||||
}
|
||||
|
||||
private int getSupportedKeepalives(NetworkCapabilities nc) throws Exception {
|
||||
// Get number of supported concurrent keepalives for testing network.
|
||||
final int[] keepalivesPerTransport = KeepaliveUtils.getSupportedKeepalives(
|
||||
mTargetContext);
|
||||
return KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
|
||||
keepalivesPerTransport, nc);
|
||||
}
|
||||
|
||||
// This class can't be private, otherwise the constants can't be static imported.
|
||||
static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback {
|
||||
// This must be larger than the alarm delay in AutomaticOnOffKeepaliveTracker.
|
||||
private static final int KEEPALIVE_TIMEOUT_MS = 10_000;
|
||||
public enum CallbackType {
|
||||
ON_STARTED,
|
||||
ON_RESUMED,
|
||||
ON_STOPPED,
|
||||
ON_PAUSED,
|
||||
ON_ERROR,
|
||||
ON_DATA_RECEIVED
|
||||
}
|
||||
private ArrayTrackRecord<CallbackType> mHistory = new ArrayTrackRecord<>();
|
||||
private ArrayTrackRecord<CallbackType>.ReadHead mEvents = mHistory.newReadHead();
|
||||
|
||||
@Override
|
||||
public void onStarted() {
|
||||
mHistory.add(ON_STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResumed() {
|
||||
mHistory.add(ON_RESUMED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopped() {
|
||||
mHistory.add(ON_STOPPED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPaused() {
|
||||
mHistory.add(ON_PAUSED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final int error) {
|
||||
mHistory.add(ON_ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataReceived() {
|
||||
mHistory.add(ON_DATA_RECEIVED);
|
||||
}
|
||||
|
||||
public CallbackType poll() {
|
||||
return mEvents.poll(KEEPALIVE_TIMEOUT_MS, it -> true);
|
||||
}
|
||||
}
|
||||
|
||||
private InetAddress getV4AddrByName(final String hostname) throws Exception {
|
||||
final InetAddress[] allAddrs = InetAddress.getAllByName(hostname);
|
||||
for (InetAddress addr : allAddrs) {
|
||||
if (addr instanceof Inet4Address) return addr;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutomaticOnOffKeepaliveModeNoClose() throws Exception {
|
||||
doTestAutomaticOnOffKeepaliveMode(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutomaticOnOffKeepaliveModeClose() throws Exception {
|
||||
doTestAutomaticOnOffKeepaliveMode(true);
|
||||
}
|
||||
|
||||
private void startKeepalive(SocketKeepalive kp, TestSocketKeepaliveCallback callback) {
|
||||
runWithShellPermissionIdentity(() -> {
|
||||
// Only SocketKeepalive.start() requires READ_DEVICE_CONFIG because feature is protected
|
||||
// by a feature flag. But also verify ON_STARTED callback received here to ensure
|
||||
// keepalive is indeed started because start() runs in the executor thread and shell
|
||||
// permission may be dropped before reading DeviceConfig.
|
||||
kp.start(10 /* intervalSec */, SocketKeepalive.FLAG_AUTOMATIC_ON_OFF, mNetwork);
|
||||
|
||||
// Verify callback status.
|
||||
assertEquals(ON_STARTED, callback.poll());
|
||||
}, READ_DEVICE_CONFIG);
|
||||
}
|
||||
|
||||
private void doTestAutomaticOnOffKeepaliveMode(final boolean closeSocket) throws Exception {
|
||||
assumeTrue(supportedHardware());
|
||||
|
||||
// Get default network first before starting VPN
|
||||
final Network defaultNetwork = mCM.getActiveNetwork();
|
||||
final TestableNetworkCallback cb = new TestableNetworkCallback();
|
||||
registerDefaultNetworkCallback(cb);
|
||||
cb.expect(CallbackEntry.AVAILABLE, defaultNetwork);
|
||||
final NetworkCapabilities cap =
|
||||
cb.expect(CallbackEntry.NETWORK_CAPS_UPDATED, defaultNetwork).getCaps();
|
||||
final LinkProperties lp =
|
||||
cb.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, defaultNetwork).getLp();
|
||||
cb.expect(CallbackEntry.BLOCKED_STATUS, defaultNetwork);
|
||||
|
||||
// Setup VPN
|
||||
final FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
|
||||
final String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
|
||||
startVpn(new String[]{"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
|
||||
new String[]{"192.0.2.0/24", "2001:db8::/32"},
|
||||
allowedApps, "" /* disallowedApplications */, null /* proxyInfo */,
|
||||
null /* underlyingNetworks */, false /* isAlwaysMetered */);
|
||||
assertSocketClosed(fd, TEST_HOST);
|
||||
|
||||
// Decrease the TCP polling timer for testing.
|
||||
runWithShellPermissionIdentity(() -> mCM.setTestLowTcpPollingTimerForKeepalive(
|
||||
System.currentTimeMillis() + TEST_TCP_POLLING_TIMER_EXPIRED_PERIOD_MS),
|
||||
NETWORK_SETTINGS);
|
||||
|
||||
// Setup keepalive
|
||||
final int supported = getSupportedKeepalives(cap);
|
||||
assumeTrue("Network " + defaultNetwork + " does not support keepalive", supported != 0);
|
||||
final InetAddress srcAddr = CollectionUtils.findFirst(lp.getAddresses(),
|
||||
it -> it instanceof Inet4Address);
|
||||
assumeTrue("This test requires native IPv4", srcAddr != null);
|
||||
|
||||
final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
|
||||
|
||||
final String origMode = runWithShellPermissionIdentity(() -> {
|
||||
final String mode = DeviceConfig.getProperty(
|
||||
DeviceConfig.NAMESPACE_CONNECTIVITY, AUTOMATIC_ON_OFF_KEEPALIVE_VERSION);
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY,
|
||||
AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
|
||||
AUTOMATIC_ON_OFF_KEEPALIVE_ENABLED, false /* makeDefault */);
|
||||
return mode;
|
||||
}, READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG);
|
||||
|
||||
final IpSecManager ipSec = mTargetContext.getSystemService(IpSecManager.class);
|
||||
SocketKeepalive kp = null;
|
||||
try (IpSecManager.UdpEncapsulationSocket nattSocket = ipSec.openUdpEncapsulationSocket()) {
|
||||
final InetAddress dstAddr = getV4AddrByName(TEST_HOST);
|
||||
assertNotNull(dstAddr);
|
||||
|
||||
// Start keepalive with dynamic keepalive mode enabled.
|
||||
final Executor executor = mTargetContext.getMainExecutor();
|
||||
kp = mCM.createSocketKeepalive(defaultNetwork, nattSocket,
|
||||
srcAddr, dstAddr, executor, callback);
|
||||
startKeepalive(kp, callback);
|
||||
|
||||
// There should be no open sockets on the VPN network, because any
|
||||
// open sockets were closed when startVpn above was called. So the
|
||||
// first TCP poll should trigger ON_PAUSED.
|
||||
assertEquals(ON_PAUSED, callback.poll());
|
||||
|
||||
final Socket s = new Socket();
|
||||
mNetwork.bindSocket(s);
|
||||
s.connect(new InetSocketAddress(dstAddr, 80));
|
||||
assertEquals(ON_RESUMED, callback.poll());
|
||||
|
||||
if (closeSocket) {
|
||||
s.close();
|
||||
assertEquals(ON_PAUSED, callback.poll());
|
||||
}
|
||||
|
||||
kp.stop();
|
||||
assertEquals(ON_STOPPED, callback.poll());
|
||||
} finally {
|
||||
if (kp != null) kp.stop();
|
||||
|
||||
runWithShellPermissionIdentity(() -> {
|
||||
DeviceConfig.setProperty(
|
||||
DeviceConfig.NAMESPACE_CONNECTIVITY,
|
||||
AUTOMATIC_ON_OFF_KEEPALIVE_VERSION,
|
||||
origMode, false);
|
||||
mCM.setTestLowTcpPollingTimerForKeepalive(0);
|
||||
}, WRITE_DEVICE_CONFIG, NETWORK_SETTINGS);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppDisallowed() throws Exception {
|
||||
assumeTrue(supportedHardware());
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.android.cts.net;
|
||||
|
||||
import android.platform.test.annotations.RequiresDevice;
|
||||
|
||||
public class HostsideVpnTests extends HostsideNetworkTestCase {
|
||||
|
||||
@Override
|
||||
|
|
@ -89,6 +91,18 @@ public class HostsideVpnTests extends HostsideNetworkTestCase {
|
|||
TEST_PKG, TEST_PKG + ".VpnTest", "testAlwaysMeteredVpnWithNullUnderlyingNetwork");
|
||||
}
|
||||
|
||||
@RequiresDevice // Keepalive is not supported on virtual hardware
|
||||
public void testAutomaticOnOffKeepaliveModeClose() throws Exception {
|
||||
runDeviceTests(
|
||||
TEST_PKG, TEST_PKG + ".VpnTest", "testAutomaticOnOffKeepaliveModeClose");
|
||||
}
|
||||
|
||||
@RequiresDevice // Keepalive is not supported on virtual hardware
|
||||
public void testAutomaticOnOffKeepaliveModeNoClose() throws Exception {
|
||||
runDeviceTests(
|
||||
TEST_PKG, TEST_PKG + ".VpnTest", "testAutomaticOnOffKeepaliveModeNoClose");
|
||||
}
|
||||
|
||||
public void testAlwaysMeteredVpnWithNonNullUnderlyingNetwork() throws Exception {
|
||||
runDeviceTests(
|
||||
TEST_PKG,
|
||||
|
|
|
|||
Loading…
Reference in a new issue