[KA01] export SocketKeepalive API for NAT-T keepalive
The new set of API replace the PacketKeepalive system API which is only specific for IPsec NAT-T keepalive packet. Allows applications to request different types of keepalives through the same set of API. By using keepalives, applications could request system to periodically send specific packets on their behalf, using hardware offload to save battery power. This change would only address interface change and test part of original functionality. After all reference of PacketKeepalive are removed, another change would be submitted to remove old API. Bug: 114151147 Test: atest FrameworksNetTests Change-Id: I3367666810acc44d5ad20e19acd4bf936e8b5d9c
This commit is contained in:
@@ -107,6 +107,8 @@ import android.net.INetworkPolicyManager;
|
||||
import android.net.INetworkStatsService;
|
||||
import android.net.InterfaceConfiguration;
|
||||
import android.net.IpPrefix;
|
||||
import android.net.IpSecManager;
|
||||
import android.net.IpSecManager.UdpEncapsulationSocket;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.MatchAllNetworkSpecifier;
|
||||
@@ -122,6 +124,7 @@ import android.net.NetworkSpecifier;
|
||||
import android.net.NetworkStack;
|
||||
import android.net.NetworkUtils;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.SocketKeepalive;
|
||||
import android.net.UidRange;
|
||||
import android.net.metrics.IpConnectivityLog;
|
||||
import android.net.shared.NetworkMonitorUtils;
|
||||
@@ -186,6 +189,8 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -402,8 +407,8 @@ public class ConnectivityServiceTest {
|
||||
private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
|
||||
private int mScore;
|
||||
private NetworkAgent mNetworkAgent;
|
||||
private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
|
||||
private int mStopKeepaliveError = PacketKeepalive.NO_KEEPALIVE;
|
||||
private int mStartKeepaliveError = SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
|
||||
private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
|
||||
private Integer mExpectedKeepaliveSlot = null;
|
||||
// Contains the redirectUrl from networkStatus(). Before reading, wait for
|
||||
// mNetworkStatusReceived.
|
||||
@@ -3548,6 +3553,80 @@ public class ConnectivityServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback {
|
||||
|
||||
public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
|
||||
|
||||
private class CallbackValue {
|
||||
public CallbackType callbackType;
|
||||
public int error;
|
||||
|
||||
CallbackValue(CallbackType type) {
|
||||
this.callbackType = type;
|
||||
this.error = SocketKeepalive.SUCCESS;
|
||||
assertTrue("onError callback must have error", type != CallbackType.ON_ERROR);
|
||||
}
|
||||
|
||||
CallbackValue(CallbackType type, int error) {
|
||||
this.callbackType = type;
|
||||
this.error = error;
|
||||
assertEquals("error can only be set for onError", type, CallbackType.ON_ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof CallbackValue
|
||||
&& this.callbackType == ((CallbackValue) o).callbackType
|
||||
&& this.error == ((CallbackValue) o).error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType,
|
||||
error);
|
||||
}
|
||||
}
|
||||
|
||||
private LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>();
|
||||
|
||||
@Override
|
||||
public void onStarted() {
|
||||
mCallbacks.add(new CallbackValue(CallbackType.ON_STARTED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopped() {
|
||||
mCallbacks.add(new CallbackValue(CallbackType.ON_STOPPED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int error) {
|
||||
mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
|
||||
}
|
||||
|
||||
private void expectCallback(CallbackValue callbackValue) {
|
||||
try {
|
||||
assertEquals(
|
||||
callbackValue,
|
||||
mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
} catch (InterruptedException e) {
|
||||
fail(callbackValue.callbackType + " callback not seen after " + TIMEOUT_MS + " ms");
|
||||
}
|
||||
}
|
||||
|
||||
public void expectStarted() {
|
||||
expectCallback(new CallbackValue(CallbackType.ON_STARTED));
|
||||
}
|
||||
|
||||
public void expectStopped() {
|
||||
expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
|
||||
}
|
||||
|
||||
public void expectError(int error) {
|
||||
expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
|
||||
}
|
||||
}
|
||||
|
||||
private Network connectKeepaliveNetwork(LinkProperties lp) {
|
||||
// Ensure the network is disconnected before we do anything.
|
||||
if (mWiFiNetworkAgent != null) {
|
||||
@@ -3694,6 +3773,144 @@ public class ConnectivityServiceTest {
|
||||
callback3.expectStopped();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNattSocketKeepalives() throws Exception {
|
||||
// TODO: 1. Move this outside of ConnectivityServiceTest.
|
||||
// 2. Add helper function to test against newSingleThreadExecutor as well as inline
|
||||
// executor.
|
||||
final int srcPort = 12345;
|
||||
final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
|
||||
final InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
|
||||
final InetAddress myIPv6 = InetAddress.getByName("2001:db8::1");
|
||||
final InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8");
|
||||
final InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888");
|
||||
|
||||
final int validKaInterval = 15;
|
||||
final int invalidKaInterval = 9;
|
||||
|
||||
final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
|
||||
final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(srcPort);
|
||||
|
||||
final Executor executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
LinkProperties lp = new LinkProperties();
|
||||
lp.setInterfaceName("wlan12");
|
||||
lp.addLinkAddress(new LinkAddress(myIPv6, 64));
|
||||
lp.addLinkAddress(new LinkAddress(myIPv4, 25));
|
||||
lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234")));
|
||||
lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
|
||||
|
||||
Network notMyNet = new Network(61234);
|
||||
Network myNet = connectKeepaliveNetwork(lp);
|
||||
|
||||
TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
|
||||
SocketKeepalive ka;
|
||||
|
||||
// Attempt to start keepalives with invalid parameters and check for errors.
|
||||
// Invalid network.
|
||||
ka = mCm.createSocketKeepalive(notMyNet, testSocket, myIPv4, dstIPv4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
|
||||
|
||||
// Invalid interval.
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
|
||||
ka.start(invalidKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_INTERVAL);
|
||||
|
||||
// Invalid destination.
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv6, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
|
||||
|
||||
// Invalid source;
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv6, dstIPv4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
|
||||
|
||||
// NAT-T is only supported for IPv4.
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv6, dstIPv6, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
|
||||
|
||||
// Sanity check before testing started keepalive.
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
|
||||
|
||||
// Check that a started keepalive can be stopped.
|
||||
mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
|
||||
ka.stop();
|
||||
callback.expectStopped();
|
||||
|
||||
// Check that deleting the IP address stops the keepalive.
|
||||
LinkProperties bogusLp = new LinkProperties(lp);
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25));
|
||||
bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25));
|
||||
mWiFiNetworkAgent.sendLinkProperties(bogusLp);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
|
||||
mWiFiNetworkAgent.sendLinkProperties(lp);
|
||||
|
||||
// Check that a started keepalive is stopped correctly when the network disconnects.
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
mWiFiNetworkAgent.disconnect();
|
||||
waitFor(mWiFiNetworkAgent.getDisconnectedCV());
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
|
||||
|
||||
// ... and that stopping it after that has no adverse effects.
|
||||
waitForIdle();
|
||||
final Network myNetAlias = myNet;
|
||||
assertNull(mCm.getNetworkCapabilities(myNetAlias));
|
||||
ka.stop();
|
||||
|
||||
// Reconnect.
|
||||
myNet = connectKeepaliveNetwork(lp);
|
||||
mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
|
||||
|
||||
// Check things work as expected when the keepalive is stopped and the network disconnects.
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
ka.stop();
|
||||
mWiFiNetworkAgent.disconnect();
|
||||
waitFor(mWiFiNetworkAgent.getDisconnectedCV());
|
||||
waitForIdle();
|
||||
callback.expectStopped();
|
||||
|
||||
// Reconnect.
|
||||
myNet = connectKeepaliveNetwork(lp);
|
||||
mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
|
||||
|
||||
// Check that keepalive slots start from 1 and increment. The first one gets slot 1.
|
||||
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
|
||||
// The second one gets slot 2.
|
||||
mWiFiNetworkAgent.setExpectedKeepaliveSlot(2);
|
||||
final UdpEncapsulationSocket testSocket2 = mIpSec.openUdpEncapsulationSocket(6789);
|
||||
TestSocketKeepaliveCallback callback2 = new TestSocketKeepaliveCallback();
|
||||
SocketKeepalive ka2 =
|
||||
mCm.createSocketKeepalive(myNet, testSocket2, myIPv4, dstIPv4, executor, callback2);
|
||||
ka2.start(validKaInterval);
|
||||
callback2.expectStarted();
|
||||
|
||||
ka.stop();
|
||||
callback.expectStopped();
|
||||
|
||||
ka2.stop();
|
||||
callback2.expectStopped();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCaptivePortalServerUrl() throws Exception {
|
||||
String url = mCm.getCaptivePortalServerUrl();
|
||||
|
||||
Reference in New Issue
Block a user