Merge "[KA04] Expose TCP socket keepalive API"

am: e5f71e4ed0

Change-Id: If8bc33ff30a529092802f9450a3772aaaa401c20
This commit is contained in:
Chalard Jean
2019-02-06 07:22:31 -08:00
committed by android-build-merger
13 changed files with 413 additions and 63 deletions

View File

@@ -68,6 +68,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -1889,7 +1890,8 @@ public class ConnectivityManager {
* @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
* changes. Must be extended by applications that use this API.
*
* @return A {@link SocketKeepalive} object, which can be used to control this keepalive object.
* @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
* given socket.
**/
public SocketKeepalive createSocketKeepalive(@NonNull Network network,
@NonNull UdpEncapsulationSocket socket,
@@ -1918,6 +1920,8 @@ public class ConnectivityManager {
* @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
* changes. Must be extended by applications that use this API.
*
* @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
* given socket.
* @hide
*/
@SystemApi
@@ -1932,6 +1936,34 @@ public class ConnectivityManager {
source, destination, executor, callback);
}
/**
* Request that keepalives be started on a TCP socket.
* The socket must be established.
*
* @param network The {@link Network} the socket is on.
* @param socket The socket that needs to be kept alive.
* @param executor The executor on which callback will be invoked. This implementation assumes
* the provided {@link Executor} runs the callbacks in sequence with no
* concurrency. Failing this, no guarantee of correctness can be made. It is
* the responsibility of the caller to ensure the executor provides this
* guarantee. A simple way of creating such an executor is with the standard
* tool {@code Executors.newSingleThreadExecutor}.
* @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
* changes. Must be extended by applications that use this API.
*
* @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
* given socket.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD)
public SocketKeepalive createSocketKeepalive(@NonNull Network network,
@NonNull Socket socket,
@NonNull Executor executor,
@NonNull Callback callback) {
return new TcpSocketKeepalive(mService, network, socket, executor, callback);
}
/**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface. An attempt to add a route that

View File

@@ -188,6 +188,9 @@ interface IConnectivityManager
int intervalSeconds, in Messenger messenger, in IBinder binder, String srcAddr,
String dstAddr);
void startTcpKeepalive(in Network network, in FileDescriptor fd, int intervalSeconds,
in Messenger messenger, in IBinder binder);
void stopKeepalive(in Network network, int slot);
String getCaptivePortalServerUrl();

View File

@@ -177,6 +177,26 @@ public abstract class NetworkAgent extends Handler {
*/
public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13;
// TODO: move the above 2 constants down so they are in order once merge conflicts are resolved
/**
* Sent by the KeepaliveTracker to NetworkAgent to add a packet filter.
*
* For TCP keepalive offloads, keepalive packets are sent by the firmware. However, because the
* remote site will send ACK packets in response to the keepalive packets, the firmware also
* needs to be configured to properly filter the ACKs to prevent the system from waking up.
* This does not happen with UDP, so this message is TCP-specific.
* arg1 = slot number of the keepalive to filter for.
* obj = the keepalive packet to send repeatedly.
*/
public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16;
/**
* Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
* {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
* arg1 = slot number of the keepalive packet filter to remove.
*/
public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
/**
* Sent by ConnectivityService to inform this network transport of signal strength thresholds
* that when crossed should trigger a system wakeup and a NetworkCapabilities update.
@@ -312,6 +332,14 @@ public abstract class NetworkAgent extends Handler {
preventAutomaticReconnect();
break;
}
case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
addKeepalivePacketFilter(msg);
break;
}
case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
removeKeepalivePacketFilter(msg);
break;
}
}
}
@@ -460,6 +488,24 @@ public abstract class NetworkAgent extends Handler {
queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, reason);
}
/**
* Called by ConnectivityService to add specific packet filter to network hardware to block
* ACKs matching the sent keepalive packets. Implementations that support this feature must
* override this method.
*/
protected void addKeepalivePacketFilter(Message msg) {
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
}
/**
* Called by ConnectivityService to remove a packet filter installed with
* {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature
* must override this method.
*/
protected void removeKeepalivePacketFilter(Message msg) {
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
}
/**
* Called by ConnectivityService to inform this network transport of signal strength thresholds
* that when crossed should trigger a system wakeup and a NetworkCapabilities update.

View File

@@ -19,6 +19,7 @@ package android.net;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -155,7 +156,7 @@ public abstract class SocketKeepalive implements AutoCloseable {
@NonNull private final SocketKeepalive.Callback mCallback;
@NonNull private final Looper mLooper;
@NonNull final Messenger mMessenger;
@NonNull Integer mSlot;
@Nullable Integer mSlot;
SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
@NonNull Executor executor, @NonNull Callback callback) {

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.annotation.NonNull;
import android.os.Binder;
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 Executor executor,
@NonNull Callback callback) {
super(service, network, executor, callback);
mSocket = socket;
}
/**
* Starts keepalives. {@code mSocket} must be a connected TCP socket.
*
* - The application must not write to or read from the socket after calling this method, until
* onDataReceived, onStopped, or onError are called. If it does, the keepalive will fail
* with {@link #ERROR_SOCKET_NOT_IDLE}, or {@code #ERROR_INVALID_SOCKET} if the socket
* experienced an error (as in poll(2) returned POLLERR); if this happens, the data received
* from the socket may be invalid, and the socket can't be recovered.
* - If the socket has data in the send or receive buffer, then this call will fail with
* {@link #ERROR_SOCKET_NOT_IDLE} and can be retried after the data has been processed.
* An app could ensure this by using an application-layer protocol where it can receive
* acknowledgement that it will go into keepalive mode. It could then go into keepalive
* mode after having read the acknowledgement, draining the socket.
*/
@Override
void startImpl(int intervalSec) {
try {
final FileDescriptor fd = mSocket.getFileDescriptor$();
mService.startTcpKeepalive(mNetwork, fd, intervalSec, mMessenger, new Binder());
} catch (RemoteException e) {
Log.e(TAG, "Error starting packet keepalive: ", e);
stopLooper();
}
}
@Override
void stopImpl() {
try {
if (mSlot != null) {
mService.stopKeepalive(mNetwork, mSlot);
}
} catch (RemoteException e) {
Log.e(TAG, "Error stopping packet keepalive: ", e);
stopLooper();
}
}
}

View File

@@ -17,6 +17,7 @@ package android.net.ip;
import android.net.ProxyInfoParcelable;
import android.net.ProvisioningConfigurationParcelable;
import android.net.TcpKeepalivePacketDataParcelable;
/** @hide */
oneway interface IIpClient {
@@ -29,4 +30,6 @@ oneway interface IIpClient {
void setTcpBufferSizes(in String tcpBufferSizes);
void setHttpProxy(in ProxyInfoParcelable proxyInfo);
void setMulticastFilter(boolean enabled);
void addKeepalivePacketFilter(int slot, in TcpKeepalivePacketDataParcelable pkt);
void removeKeepalivePacketFilter(int slot);
}