Merge "Fix SocketKeepalive APIs which do not meet API review requirement" am: 66bc22760a
am: 7246f1a563
Change-Id: Iead39e877aec917ff3c409fda1ff0c4dbaa21fcb
This commit is contained in:
@@ -44,6 +44,7 @@ import android.os.INetworkManagementService;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
@@ -64,6 +65,8 @@ import com.android.internal.util.Protocol;
|
||||
import libcore.net.event.NetworkEventDispatcher;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.net.InetAddress;
|
||||
@@ -1923,14 +1926,22 @@ public class ConnectivityManager {
|
||||
* @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
|
||||
* given socket.
|
||||
**/
|
||||
public SocketKeepalive createSocketKeepalive(@NonNull Network network,
|
||||
public @NonNull SocketKeepalive createSocketKeepalive(@NonNull Network network,
|
||||
@NonNull UdpEncapsulationSocket socket,
|
||||
@NonNull InetAddress source,
|
||||
@NonNull InetAddress destination,
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull Callback callback) {
|
||||
return new NattSocketKeepalive(mService, network, socket.getFileDescriptor(),
|
||||
socket.getResourceId(), source, destination, executor, callback);
|
||||
ParcelFileDescriptor dup;
|
||||
try {
|
||||
dup = ParcelFileDescriptor.dup(socket.getFileDescriptor());
|
||||
} catch (IOException ignored) {
|
||||
// Construct an invalid fd, so that if the user later calls start(), it will fail with
|
||||
// ERROR_INVALID_SOCKET.
|
||||
dup = new ParcelFileDescriptor(new FileDescriptor());
|
||||
}
|
||||
return new NattSocketKeepalive(mService, network, dup, socket.getResourceId(), source,
|
||||
destination, executor, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1938,9 +1949,9 @@ public class ConnectivityManager {
|
||||
* by system apps which don't use IpSecService to create {@link UdpEncapsulationSocket}.
|
||||
*
|
||||
* @param network The {@link Network} the socket is on.
|
||||
* @param fd The {@link FileDescriptor} that needs to be kept alive. The provided
|
||||
* {@link FileDescriptor} must be bound to a port and the keepalives will be sent from
|
||||
* that port.
|
||||
* @param pfd The {@link ParcelFileDescriptor} that needs to be kept alive. The provided
|
||||
* {@link ParcelFileDescriptor} must be bound to a port and the keepalives will be sent
|
||||
* from that port.
|
||||
* @param source The source address of the {@link UdpEncapsulationSocket}.
|
||||
* @param destination The destination address of the {@link UdpEncapsulationSocket}. The
|
||||
* keepalive packets will always be sent to port 4500 of the given {@code destination}.
|
||||
@@ -1956,14 +1967,22 @@ public class ConnectivityManager {
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD)
|
||||
public SocketKeepalive createNattKeepalive(@NonNull Network network,
|
||||
@NonNull FileDescriptor fd,
|
||||
public @NonNull SocketKeepalive createNattKeepalive(@NonNull Network network,
|
||||
@NonNull ParcelFileDescriptor pfd,
|
||||
@NonNull InetAddress source,
|
||||
@NonNull InetAddress destination,
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull Callback callback) {
|
||||
return new NattSocketKeepalive(mService, network, fd, INVALID_RESOURCE_ID /* Unused */,
|
||||
source, destination, executor, callback);
|
||||
ParcelFileDescriptor dup;
|
||||
try {
|
||||
dup = pfd.dup();
|
||||
} catch (IOException ignored) {
|
||||
// Construct an invalid fd, so that if the user later calls start(), it will fail with
|
||||
// ERROR_INVALID_SOCKET.
|
||||
dup = new ParcelFileDescriptor(new FileDescriptor());
|
||||
}
|
||||
return new NattSocketKeepalive(mService, network, dup,
|
||||
INVALID_RESOURCE_ID /* Unused */, source, destination, executor, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1987,11 +2006,19 @@ public class ConnectivityManager {
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD)
|
||||
public SocketKeepalive createSocketKeepalive(@NonNull Network network,
|
||||
public @NonNull SocketKeepalive createSocketKeepalive(@NonNull Network network,
|
||||
@NonNull Socket socket,
|
||||
@NonNull Executor executor,
|
||||
@NonNull Callback callback) {
|
||||
return new TcpSocketKeepalive(mService, network, socket, executor, callback);
|
||||
ParcelFileDescriptor dup;
|
||||
try {
|
||||
dup = ParcelFileDescriptor.fromSocket(socket);
|
||||
} catch (UncheckedIOException ignored) {
|
||||
// Construct an invalid fd, so that if the user later calls start(), it will fail with
|
||||
// ERROR_INVALID_SOCKET.
|
||||
dup = new ParcelFileDescriptor(new FileDescriptor());
|
||||
}
|
||||
return new TcpSocketKeepalive(mService, network, dup, executor, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3323,7 +3350,7 @@ public class ConnectivityManager {
|
||||
* @param network The {@link Network} whose blocked status has changed.
|
||||
* @param blocked The blocked status of this {@link Network}.
|
||||
*/
|
||||
public void onBlockedStatusChanged(Network network, boolean blocked) {}
|
||||
public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
|
||||
|
||||
private NetworkRequest networkRequest;
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
package android.net;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.net.InetAddress;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -31,21 +31,19 @@ public final class NattSocketKeepalive extends SocketKeepalive {
|
||||
|
||||
@NonNull private final InetAddress mSource;
|
||||
@NonNull private final InetAddress mDestination;
|
||||
@NonNull private final FileDescriptor mFd;
|
||||
private final int mResourceId;
|
||||
|
||||
NattSocketKeepalive(@NonNull IConnectivityManager service,
|
||||
@NonNull Network network,
|
||||
@NonNull FileDescriptor fd,
|
||||
@NonNull ParcelFileDescriptor pfd,
|
||||
int resourceId,
|
||||
@NonNull InetAddress source,
|
||||
@NonNull InetAddress destination,
|
||||
@NonNull Executor executor,
|
||||
@NonNull Callback callback) {
|
||||
super(service, network, executor, callback);
|
||||
super(service, network, pfd, executor, callback);
|
||||
mSource = source;
|
||||
mDestination = destination;
|
||||
mFd = fd;
|
||||
mResourceId = resourceId;
|
||||
}
|
||||
|
||||
@@ -53,8 +51,8 @@ public final class NattSocketKeepalive extends SocketKeepalive {
|
||||
void startImpl(int intervalSec) {
|
||||
mExecutor.execute(() -> {
|
||||
try {
|
||||
mService.startNattKeepaliveWithFd(mNetwork, mFd, mResourceId, intervalSec,
|
||||
mCallback,
|
||||
mService.startNattKeepaliveWithFd(mNetwork, mPfd.getFileDescriptor(), mResourceId,
|
||||
intervalSec, mCallback,
|
||||
mSource.getHostAddress(), mDestination.getHostAddress());
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error starting socket keepalive: ", e);
|
||||
@@ -75,6 +73,5 @@ public final class NattSocketKeepalive extends SocketKeepalive {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,14 +488,14 @@ public abstract class NetworkAgent extends Handler {
|
||||
* Requests that the network hardware send the specified packet at the specified interval.
|
||||
*/
|
||||
protected void startSocketKeepalive(Message msg) {
|
||||
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
|
||||
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the network hardware stops sending keepalive packets.
|
||||
*/
|
||||
protected void stopSocketKeepalive(Message msg) {
|
||||
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
|
||||
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -511,7 +511,7 @@ public abstract class NetworkAgent extends Handler {
|
||||
* override this method.
|
||||
*/
|
||||
protected void addKeepalivePacketFilter(Message msg) {
|
||||
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
|
||||
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -520,7 +520,7 @@ public abstract class NetworkAgent extends Handler {
|
||||
* must override this method.
|
||||
*/
|
||||
protected void removeKeepalivePacketFilter(Message msg) {
|
||||
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
|
||||
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,8 +21,10 @@ import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Binder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -73,10 +75,15 @@ public abstract class SocketKeepalive implements AutoCloseable {
|
||||
/** The target socket is not idle. */
|
||||
public static final int ERROR_SOCKET_NOT_IDLE = -26;
|
||||
|
||||
/** The hardware does not support this request. */
|
||||
public static final int ERROR_HARDWARE_UNSUPPORTED = -30;
|
||||
/** The device does not support this request. */
|
||||
public static final int ERROR_UNSUPPORTED = -30;
|
||||
/** @hide TODO: delete when telephony code has been updated. */
|
||||
public static final int ERROR_HARDWARE_UNSUPPORTED = ERROR_UNSUPPORTED;
|
||||
/** The hardware returned an error. */
|
||||
public static final int ERROR_HARDWARE_ERROR = -31;
|
||||
/** The limitation of resource is reached. */
|
||||
public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
|
||||
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@@ -147,15 +154,18 @@ public abstract class SocketKeepalive implements AutoCloseable {
|
||||
|
||||
@NonNull final IConnectivityManager mService;
|
||||
@NonNull final Network mNetwork;
|
||||
@NonNull final ParcelFileDescriptor mPfd;
|
||||
@NonNull final Executor mExecutor;
|
||||
@NonNull final ISocketKeepaliveCallback mCallback;
|
||||
// TODO: remove slot since mCallback could be used to identify which keepalive to stop.
|
||||
@Nullable Integer mSlot;
|
||||
|
||||
SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
|
||||
@NonNull ParcelFileDescriptor pfd,
|
||||
@NonNull Executor executor, @NonNull Callback callback) {
|
||||
mService = service;
|
||||
mNetwork = network;
|
||||
mPfd = pfd;
|
||||
mExecutor = executor;
|
||||
mCallback = new ISocketKeepaliveCallback.Stub() {
|
||||
@Override
|
||||
@@ -233,6 +243,11 @@ public abstract class SocketKeepalive implements AutoCloseable {
|
||||
@Override
|
||||
public final void close() {
|
||||
stop();
|
||||
try {
|
||||
mPfd.close();
|
||||
} catch (IOException e) {
|
||||
// Nothing much can be done.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,25 +17,22 @@
|
||||
package android.net;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/** @hide */
|
||||
final class TcpSocketKeepalive extends SocketKeepalive {
|
||||
|
||||
private final Socket mSocket;
|
||||
|
||||
TcpSocketKeepalive(@NonNull IConnectivityManager service,
|
||||
@NonNull Network network,
|
||||
@NonNull Socket socket,
|
||||
@NonNull ParcelFileDescriptor pfd,
|
||||
@NonNull Executor executor,
|
||||
@NonNull Callback callback) {
|
||||
super(service, network, executor, callback);
|
||||
mSocket = socket;
|
||||
super(service, network, pfd, executor, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,7 +54,7 @@ final class TcpSocketKeepalive extends SocketKeepalive {
|
||||
void startImpl(int intervalSec) {
|
||||
mExecutor.execute(() -> {
|
||||
try {
|
||||
final FileDescriptor fd = mSocket.getFileDescriptor$();
|
||||
final FileDescriptor fd = mPfd.getFileDescriptor();
|
||||
mService.startTcpKeepalive(mNetwork, fd, intervalSec, mCallback);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error starting packet keepalive: ", e);
|
||||
|
||||
@@ -988,7 +988,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
|
||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
|
||||
mKeepaliveTracker = new KeepaliveTracker(mHandler);
|
||||
mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler);
|
||||
mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager,
|
||||
mContext.getSystemService(NotificationManager.class));
|
||||
|
||||
@@ -6702,7 +6702,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
ISocketKeepaliveCallback cb, String srcAddr, int srcPort, String dstAddr) {
|
||||
enforceKeepalivePermission();
|
||||
mKeepaliveTracker.startNattKeepalive(
|
||||
getNetworkAgentInfoForNetwork(network),
|
||||
getNetworkAgentInfoForNetwork(network), null /* fd */,
|
||||
intervalSeconds, cb,
|
||||
srcAddr, srcPort, dstAddr, NattSocketKeepalive.NATT_PORT);
|
||||
}
|
||||
@@ -6711,7 +6711,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
public void startNattKeepaliveWithFd(Network network, FileDescriptor fd, int resourceId,
|
||||
int intervalSeconds, ISocketKeepaliveCallback cb, String srcAddr,
|
||||
String dstAddr) {
|
||||
enforceKeepalivePermission();
|
||||
mKeepaliveTracker.startNattKeepalive(
|
||||
getNetworkAgentInfoForNetwork(network), fd, resourceId,
|
||||
intervalSeconds, cb,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.net.NattSocketKeepalive.NATT_PORT;
|
||||
import static android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER;
|
||||
import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER;
|
||||
@@ -23,6 +24,7 @@ import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
|
||||
import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE;
|
||||
import static android.net.SocketKeepalive.BINDER_DIED;
|
||||
import static android.net.SocketKeepalive.DATA_RECEIVED;
|
||||
import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
|
||||
import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL;
|
||||
import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
|
||||
import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK;
|
||||
@@ -34,6 +36,7 @@ import static android.net.SocketKeepalive.SUCCESS;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.net.ISocketKeepaliveCallback;
|
||||
import android.net.KeepalivePacketData;
|
||||
import android.net.NattKeepalivePacketData;
|
||||
@@ -84,10 +87,13 @@ public class KeepaliveTracker {
|
||||
private final Handler mConnectivityServiceHandler;
|
||||
@NonNull
|
||||
private final TcpKeepaliveController mTcpController;
|
||||
@NonNull
|
||||
private final Context mContext;
|
||||
|
||||
public KeepaliveTracker(Handler handler) {
|
||||
public KeepaliveTracker(Context context, Handler handler) {
|
||||
mConnectivityServiceHandler = handler;
|
||||
mTcpController = new TcpKeepaliveController(handler);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,6 +107,7 @@ public class KeepaliveTracker {
|
||||
private final ISocketKeepaliveCallback mCallback;
|
||||
private final int mUid;
|
||||
private final int mPid;
|
||||
private final boolean mPrivileged;
|
||||
private final NetworkAgentInfo mNai;
|
||||
private final int mType;
|
||||
private final FileDescriptor mFd;
|
||||
@@ -108,6 +115,11 @@ public class KeepaliveTracker {
|
||||
public static final int TYPE_NATT = 1;
|
||||
public static final int TYPE_TCP = 2;
|
||||
|
||||
// Max allowed unprivileged keepalive slots per network. Caller's permission will be
|
||||
// enforced if number of existing keepalives reach this limit.
|
||||
// TODO: consider making this limit configurable via resources.
|
||||
private static final int MAX_UNPRIVILEGED_SLOTS = 3;
|
||||
|
||||
// Keepalive slot. A small integer that identifies this keepalive among the ones handled
|
||||
// by this network.
|
||||
private int mSlot = NO_KEEPALIVE;
|
||||
@@ -127,16 +139,33 @@ public class KeepaliveTracker {
|
||||
@NonNull KeepalivePacketData packet,
|
||||
int interval,
|
||||
int type,
|
||||
@NonNull FileDescriptor fd) {
|
||||
@Nullable FileDescriptor fd) throws InvalidSocketException {
|
||||
mCallback = callback;
|
||||
mPid = Binder.getCallingPid();
|
||||
mUid = Binder.getCallingUid();
|
||||
mPrivileged = (PERMISSION_GRANTED == mContext.checkPermission(PERMISSION, mPid, mUid));
|
||||
|
||||
mNai = nai;
|
||||
mPacket = packet;
|
||||
mInterval = interval;
|
||||
mType = type;
|
||||
mFd = fd;
|
||||
|
||||
// For SocketKeepalive, a dup of fd is kept in mFd so the source port from which the
|
||||
// keepalives are sent cannot be reused by another app even if the fd gets closed by
|
||||
// the user. A null is acceptable here for backward compatibility of PacketKeepalive
|
||||
// API.
|
||||
// TODO: don't accept null fd after legacy packetKeepalive API is removed.
|
||||
try {
|
||||
if (fd != null) {
|
||||
mFd = Os.dup(fd);
|
||||
} else {
|
||||
Log.d(TAG, "uid/pid " + mUid + "/" + mPid + " calls with null fd");
|
||||
mFd = null;
|
||||
}
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, "Cannot dup fd: ", e);
|
||||
throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
|
||||
}
|
||||
|
||||
try {
|
||||
mCallback.asBinder().linkToDeath(this, 0);
|
||||
@@ -167,7 +196,7 @@ public class KeepaliveTracker {
|
||||
+ "->"
|
||||
+ IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)
|
||||
+ " interval=" + mInterval
|
||||
+ " uid=" + mUid + " pid=" + mPid
|
||||
+ " uid=" + mUid + " pid=" + mPid + " privileged=" + mPrivileged
|
||||
+ " packetData=" + HexDump.toHexString(mPacket.getPacket())
|
||||
+ " ]";
|
||||
}
|
||||
@@ -207,9 +236,27 @@ public class KeepaliveTracker {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private int checkPermission() {
|
||||
final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai);
|
||||
int unprivilegedCount = 0;
|
||||
if (networkKeepalives == null) {
|
||||
return ERROR_INVALID_NETWORK;
|
||||
}
|
||||
for (KeepaliveInfo ki : networkKeepalives.values()) {
|
||||
if (!ki.mPrivileged) {
|
||||
unprivilegedCount++;
|
||||
}
|
||||
if (unprivilegedCount >= MAX_UNPRIVILEGED_SLOTS) {
|
||||
return mPrivileged ? SUCCESS : ERROR_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private int isValid() {
|
||||
synchronized (mNai) {
|
||||
int error = checkInterval();
|
||||
if (error == SUCCESS) error = checkPermission();
|
||||
if (error == SUCCESS) error = checkNetworkConnected();
|
||||
if (error == SUCCESS) error = checkSourceAddress();
|
||||
return error;
|
||||
@@ -272,6 +319,18 @@ public class KeepaliveTracker {
|
||||
}
|
||||
}
|
||||
|
||||
// Close the duplicated fd that maintains the lifecycle of socket whenever
|
||||
// keepalive is running.
|
||||
if (mFd != null) {
|
||||
try {
|
||||
Os.close(mFd);
|
||||
} catch (ErrnoException e) {
|
||||
// This should not happen since system server controls the lifecycle of fd when
|
||||
// keepalive offload is running.
|
||||
Log.wtf(TAG, "Error closing fd for keepalive " + mSlot + ": " + e);
|
||||
}
|
||||
}
|
||||
|
||||
if (reason == SUCCESS) {
|
||||
try {
|
||||
mCallback.onStopped();
|
||||
@@ -355,8 +414,9 @@ public class KeepaliveTracker {
|
||||
return;
|
||||
}
|
||||
ki.stop(reason);
|
||||
Log.d(TAG, "Stopped keepalive " + slot + " on " + networkName);
|
||||
networkKeepalives.remove(slot);
|
||||
Log.d(TAG, "Stopped keepalive " + slot + " on " + networkName + ", "
|
||||
+ networkKeepalives.size() + " remains.");
|
||||
if (networkKeepalives.isEmpty()) {
|
||||
mKeepalives.remove(nai);
|
||||
}
|
||||
@@ -389,7 +449,8 @@ public class KeepaliveTracker {
|
||||
ki = mKeepalives.get(nai).get(slot);
|
||||
} catch(NullPointerException e) {}
|
||||
if (ki == null) {
|
||||
Log.e(TAG, "Event for unknown keepalive " + slot + " on " + nai.name());
|
||||
Log.e(TAG, "Event " + message.what + " for unknown keepalive " + slot + " on "
|
||||
+ nai.name());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -437,6 +498,7 @@ public class KeepaliveTracker {
|
||||
* {@link android.net.SocketKeepalive}.
|
||||
**/
|
||||
public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
|
||||
@Nullable FileDescriptor fd,
|
||||
int intervalSeconds,
|
||||
@NonNull ISocketKeepaliveCallback cb,
|
||||
@NonNull String srcAddrString,
|
||||
@@ -465,8 +527,14 @@ public class KeepaliveTracker {
|
||||
notifyErrorCallback(cb, e.error);
|
||||
return;
|
||||
}
|
||||
KeepaliveInfo ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
|
||||
KeepaliveInfo.TYPE_NATT, null);
|
||||
KeepaliveInfo ki = null;
|
||||
try {
|
||||
ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
|
||||
KeepaliveInfo.TYPE_NATT, fd);
|
||||
} catch (InvalidSocketException e) {
|
||||
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "Created keepalive: " + ki.toString());
|
||||
mConnectivityServiceHandler.obtainMessage(
|
||||
NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
|
||||
@@ -498,9 +566,14 @@ public class KeepaliveTracker {
|
||||
notifyErrorCallback(cb, e.error);
|
||||
return;
|
||||
}
|
||||
|
||||
KeepaliveInfo ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
|
||||
KeepaliveInfo.TYPE_TCP, fd);
|
||||
KeepaliveInfo ki = null;
|
||||
try {
|
||||
ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
|
||||
KeepaliveInfo.TYPE_TCP, fd);
|
||||
} catch (InvalidSocketException e) {
|
||||
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "Created keepalive: " + ki.toString());
|
||||
mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
|
||||
}
|
||||
@@ -535,7 +608,7 @@ public class KeepaliveTracker {
|
||||
}
|
||||
|
||||
// Forward request to old API.
|
||||
startNattKeepalive(nai, intervalSeconds, cb, srcAddrString, srcPort,
|
||||
startNattKeepalive(nai, fd, intervalSeconds, cb, srcAddrString, srcPort,
|
||||
dstAddrString, dstPort);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import static android.net.SocketKeepalive.DATA_RECEIVED;
|
||||
import static android.net.SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
|
||||
import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
|
||||
import static android.net.SocketKeepalive.ERROR_SOCKET_NOT_IDLE;
|
||||
import static android.net.SocketKeepalive.ERROR_UNSUPPORTED;
|
||||
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
|
||||
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
|
||||
import static android.system.OsConstants.ENOPROTOOPT;
|
||||
@@ -197,8 +197,8 @@ public class TcpKeepaliveController {
|
||||
Log.e(TAG, "Exception reading TCP state from socket", e);
|
||||
if (e.errno == ENOPROTOOPT) {
|
||||
// ENOPROTOOPT may happen in kernel version lower than 4.8.
|
||||
// Treat it as ERROR_HARDWARE_UNSUPPORTED.
|
||||
throw new InvalidSocketException(ERROR_HARDWARE_UNSUPPORTED, e);
|
||||
// Treat it as ERROR_UNSUPPORTED.
|
||||
throw new InvalidSocketException(ERROR_UNSUPPORTED, e);
|
||||
} else {
|
||||
throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
|
||||
}
|
||||
|
||||
@@ -144,12 +144,14 @@ import android.os.INetworkManagementService;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.system.Os;
|
||||
import android.test.mock.MockContentResolver;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
@@ -188,6 +190,8 @@ import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -421,7 +425,7 @@ public class ConnectivityServiceTest {
|
||||
private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
|
||||
private int mScore;
|
||||
private NetworkAgent mNetworkAgent;
|
||||
private int mStartKeepaliveError = SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
|
||||
private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
|
||||
private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
|
||||
private Integer mExpectedKeepaliveSlot = null;
|
||||
// Contains the redirectUrl from networkStatus(). Before reading, wait for
|
||||
@@ -4032,6 +4036,7 @@ public class ConnectivityServiceTest {
|
||||
runTestWithSerialExecutors(executor -> {
|
||||
try {
|
||||
doTestNattSocketKeepalivesWithExecutor(executor);
|
||||
doTestNattSocketKeepalivesFdWithExecutor(executor);
|
||||
} catch (Exception e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
@@ -4041,6 +4046,8 @@ public class ConnectivityServiceTest {
|
||||
private void doTestNattSocketKeepalivesWithExecutor(Executor executor) throws Exception {
|
||||
// TODO: 1. Move this outside of ConnectivityServiceTest.
|
||||
// 2. Make test to verify that Nat-T keepalive socket is created by IpSecService.
|
||||
// 3. Mock ipsec service.
|
||||
// 4. Find a free port instead of a fixed port.
|
||||
final int srcPort = 12345;
|
||||
final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
|
||||
final InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
|
||||
@@ -4065,89 +4072,106 @@ public class ConnectivityServiceTest {
|
||||
Network myNet = connectKeepaliveNetwork(lp);
|
||||
|
||||
TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor);
|
||||
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);
|
||||
try (SocketKeepalive 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);
|
||||
try (SocketKeepalive 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);
|
||||
try (SocketKeepalive 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);
|
||||
try (SocketKeepalive 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);
|
||||
try (SocketKeepalive 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);
|
||||
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
||||
myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_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();
|
||||
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
||||
myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
|
||||
ka.stop();
|
||||
callback.expectStopped();
|
||||
|
||||
// Check that keepalive could be restarted.
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
ka.stop();
|
||||
callback.expectStopped();
|
||||
// Check that keepalive could be restarted.
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
ka.stop();
|
||||
callback.expectStopped();
|
||||
|
||||
// Check that keepalive can be restarted without waiting for callback.
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
ka.stop();
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStopped();
|
||||
callback.expectStarted();
|
||||
ka.stop();
|
||||
callback.expectStopped();
|
||||
// Check that keepalive can be restarted without waiting for callback.
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
ka.stop();
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStopped();
|
||||
callback.expectStarted();
|
||||
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);
|
||||
try (SocketKeepalive 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);
|
||||
try (SocketKeepalive 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();
|
||||
callback.assertNoCallback();
|
||||
// ... and that stopping it after that has no adverse effects.
|
||||
waitForIdle();
|
||||
final Network myNetAlias = myNet;
|
||||
assertNull(mCm.getNetworkCapabilities(myNetAlias));
|
||||
ka.stop();
|
||||
callback.assertNoCallback();
|
||||
}
|
||||
|
||||
// Reconnect.
|
||||
myNet = connectKeepaliveNetwork(lp);
|
||||
@@ -4155,27 +4179,30 @@ public class ConnectivityServiceTest {
|
||||
|
||||
// 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();
|
||||
try (SocketKeepalive 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(executor);
|
||||
SocketKeepalive ka2 =
|
||||
mCm.createSocketKeepalive(myNet, testSocket2, myIPv4, dstIPv4, executor, callback2);
|
||||
ka2.start(validKaInterval);
|
||||
callback2.expectStarted();
|
||||
// The second one gets slot 2.
|
||||
mWiFiNetworkAgent.setExpectedKeepaliveSlot(2);
|
||||
final UdpEncapsulationSocket testSocket2 = mIpSec.openUdpEncapsulationSocket(6789);
|
||||
TestSocketKeepaliveCallback callback2 = new TestSocketKeepaliveCallback(executor);
|
||||
try (SocketKeepalive ka2 = mCm.createSocketKeepalive(
|
||||
myNet, testSocket2, myIPv4, dstIPv4, executor, callback2)) {
|
||||
ka2.start(validKaInterval);
|
||||
callback2.expectStarted();
|
||||
|
||||
ka.stop();
|
||||
callback.expectStopped();
|
||||
ka.stop();
|
||||
callback.expectStopped();
|
||||
|
||||
ka2.stop();
|
||||
callback2.expectStopped();
|
||||
ka2.stop();
|
||||
callback2.expectStopped();
|
||||
|
||||
testSocket.close();
|
||||
testSocket2.close();
|
||||
testSocket.close();
|
||||
testSocket2.close();
|
||||
}
|
||||
}
|
||||
|
||||
mWiFiNetworkAgent.disconnect();
|
||||
waitFor(mWiFiNetworkAgent.getDisconnectedCV());
|
||||
@@ -4200,7 +4227,6 @@ public class ConnectivityServiceTest {
|
||||
final InetAddress myIPv6 = InetAddress.getByName("::1");
|
||||
|
||||
final int validKaInterval = 15;
|
||||
final int invalidKaInterval = 9;
|
||||
|
||||
final LinkProperties lp = new LinkProperties();
|
||||
lp.setInterfaceName("wlan12");
|
||||
@@ -4216,37 +4242,46 @@ public class ConnectivityServiceTest {
|
||||
final Socket testSocketV6 = new Socket();
|
||||
|
||||
TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor);
|
||||
SocketKeepalive ka;
|
||||
|
||||
// Attempt to start Tcp keepalives with invalid parameters and check for errors.
|
||||
// Invalid network.
|
||||
ka = mCm.createSocketKeepalive(notMyNet, testSocketV4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
|
||||
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
||||
notMyNet, testSocketV4, executor, callback)) {
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
|
||||
}
|
||||
|
||||
// Invalid Socket (socket is not bound with IPv4 address).
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocketV4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET);
|
||||
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
||||
myNet, testSocketV4, executor, callback)) {
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET);
|
||||
}
|
||||
|
||||
// Invalid Socket (socket is not bound with IPv6 address).
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocketV6, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET);
|
||||
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
||||
myNet, testSocketV6, executor, callback)) {
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET);
|
||||
}
|
||||
|
||||
// Bind the socket address
|
||||
testSocketV4.bind(new InetSocketAddress(myIPv4, srcPortV4));
|
||||
testSocketV6.bind(new InetSocketAddress(myIPv6, srcPortV6));
|
||||
|
||||
// Invalid Socket (socket is bound with IPv4 address).
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocketV4, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET);
|
||||
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
||||
myNet, testSocketV4, executor, callback)) {
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET);
|
||||
}
|
||||
|
||||
// Invalid Socket (socket is bound with IPv6 address).
|
||||
ka = mCm.createSocketKeepalive(myNet, testSocketV6, executor, callback);
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET);
|
||||
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
||||
myNet, testSocketV6, executor, callback)) {
|
||||
ka.start(validKaInterval);
|
||||
callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET);
|
||||
}
|
||||
|
||||
testSocketV4.close();
|
||||
testSocketV6.close();
|
||||
@@ -4256,6 +4291,66 @@ public class ConnectivityServiceTest {
|
||||
mWiFiNetworkAgent = null;
|
||||
}
|
||||
|
||||
private void doTestNattSocketKeepalivesFdWithExecutor(Executor executor) throws Exception {
|
||||
final int srcPort = 12345;
|
||||
final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
|
||||
final InetAddress anyIPv4 = InetAddress.getByName("0.0.0.0");
|
||||
final InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8");
|
||||
final int validKaInterval = 15;
|
||||
|
||||
// Prepare the target network.
|
||||
LinkProperties lp = new LinkProperties();
|
||||
lp.setInterfaceName("wlan12");
|
||||
lp.addLinkAddress(new LinkAddress(myIPv4, 25));
|
||||
lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
|
||||
Network myNet = connectKeepaliveNetwork(lp);
|
||||
mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
|
||||
mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
|
||||
|
||||
TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor);
|
||||
|
||||
// Prepare the target file descriptor, keep only one instance.
|
||||
final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
|
||||
final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(srcPort);
|
||||
final ParcelFileDescriptor testPfd =
|
||||
ParcelFileDescriptor.dup(testSocket.getFileDescriptor());
|
||||
testSocket.close();
|
||||
assertTrue(isUdpPortInUse(srcPort));
|
||||
|
||||
// Start keepalive and explicit make the variable goes out of scope with try-with-resources
|
||||
// block.
|
||||
try (SocketKeepalive ka = mCm.createNattKeepalive(
|
||||
myNet, testPfd, myIPv4, dstIPv4, executor, callback)) {
|
||||
ka.start(validKaInterval);
|
||||
callback.expectStarted();
|
||||
ka.stop();
|
||||
callback.expectStopped();
|
||||
}
|
||||
|
||||
// Check that the ParcelFileDescriptor is still valid after keepalive stopped,
|
||||
// ErrnoException with EBADF will be thrown if the socket is closed when checking local
|
||||
// address.
|
||||
assertTrue(isUdpPortInUse(srcPort));
|
||||
final InetSocketAddress sa =
|
||||
(InetSocketAddress) Os.getsockname(testPfd.getFileDescriptor());
|
||||
assertEquals(anyIPv4, sa.getAddress());
|
||||
|
||||
testPfd.close();
|
||||
assertFalse(isUdpPortInUse(srcPort));
|
||||
|
||||
mWiFiNetworkAgent.disconnect();
|
||||
waitFor(mWiFiNetworkAgent.getDisconnectedCV());
|
||||
mWiFiNetworkAgent = null;
|
||||
}
|
||||
|
||||
private static boolean isUdpPortInUse(int port) {
|
||||
try (DatagramSocket ignored = new DatagramSocket(port)) {
|
||||
return false;
|
||||
} catch (IOException ignored) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCaptivePortalServerUrl() throws Exception {
|
||||
String url = mCm.getCaptivePortalServerUrl();
|
||||
|
||||
Reference in New Issue
Block a user