[DK2]Add new SocketKeepalive.start to dynamically control keepalive
Add SocketKeepalive.start with parameter to enable dynamic keepalive mode based on the existence of TCP connections. This supports IPSec mode to notify KeepaliveTracker to disable keepalive when keepalive is unnecessary to improve battery life. Keepalive is controlled by periodically TCP socket status check for both enable and disable. This is a transition commit and is expected to be updated based on the socket creation or destroy. Bug: 259000745 Test: m ; atest FrameworksNetTests Change-Id: Ie4d598d69a73c4931c7d0b6dfde0e459e5dca6b4
This commit is contained in:
committed by
Chalard Jean
parent
3d60bacfa0
commit
9ef4ffe8d4
@@ -43,7 +43,9 @@
|
|||||||
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
|
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
|
|
||||||
|
<!-- Sending non-protected broadcast from system uid is not allowed. -->
|
||||||
<protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" />
|
<protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" />
|
||||||
|
<protected-broadcast android:name="com.android.server.connectivity.KeepaliveTracker.TCP_POLLING_ALARM" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:process="com.android.networkstack.process"
|
android:process="com.android.networkstack.process"
|
||||||
|
|||||||
@@ -470,7 +470,9 @@ package android.net {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract class SocketKeepalive implements java.lang.AutoCloseable {
|
public abstract class SocketKeepalive implements java.lang.AutoCloseable {
|
||||||
|
method public final void start(@IntRange(from=0xa, to=0xe10) int, int);
|
||||||
field public static final int ERROR_NO_SUCH_SLOT = -33; // 0xffffffdf
|
field public static final int ERROR_NO_SUCH_SLOT = -33; // 0xffffffdf
|
||||||
|
field public static final int FLAG_AUTOMATIC_ON_OFF = 1; // 0x1
|
||||||
field public static final int SUCCESS = 0; // 0x0
|
field public static final int SUCCESS = 0; // 0x0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ interface IConnectivityManager
|
|||||||
|
|
||||||
void startNattKeepaliveWithFd(in Network network, in ParcelFileDescriptor pfd, int resourceId,
|
void startNattKeepaliveWithFd(in Network network, in ParcelFileDescriptor pfd, int resourceId,
|
||||||
int intervalSeconds, in ISocketKeepaliveCallback cb, String srcAddr,
|
int intervalSeconds, in ISocketKeepaliveCallback cb, String srcAddr,
|
||||||
String dstAddr);
|
String dstAddr, boolean automaticOnOffKeepalives);
|
||||||
|
|
||||||
void startTcpKeepalive(in Network network, in ParcelFileDescriptor pfd, int intervalSeconds,
|
void startTcpKeepalive(in Network network, in ParcelFileDescriptor pfd, int intervalSeconds,
|
||||||
in ISocketKeepaliveCallback cb);
|
in ISocketKeepaliveCallback cb);
|
||||||
|
|||||||
@@ -47,13 +47,39 @@ public final class NattSocketKeepalive extends SocketKeepalive {
|
|||||||
mResourceId = resourceId;
|
mResourceId = resourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request that keepalive be started with the given {@code intervalSec}.
|
||||||
|
*
|
||||||
|
* When a VPN is running with the network for this keepalive as its underlying network, the
|
||||||
|
* system can monitor the TCP connections on that VPN to determine whether this keepalive is
|
||||||
|
* necessary. To enable this behavior, pass {@link SocketKeepalive#FLAG_AUTOMATIC_ON_OFF} into
|
||||||
|
* the flags. When this is enabled, the system will disable sending keepalive packets when
|
||||||
|
* there are no TCP connections over the VPN(s) running over this network to save battery, and
|
||||||
|
* restart sending them as soon as any TCP connection is opened over one of the VPN networks.
|
||||||
|
* When no VPN is running on top of this network, this flag has no effect, i.e. the keepalives
|
||||||
|
* are always sent with the specified interval.
|
||||||
|
*
|
||||||
|
* Also {@see SocketKeepalive}.
|
||||||
|
*
|
||||||
|
* @param intervalSec The target interval in seconds between keepalive packet transmissions.
|
||||||
|
* The interval should be between 10 seconds and 3600 seconds. Otherwise,
|
||||||
|
* the supplied {@link Callback} will see a call to
|
||||||
|
* {@link Callback#onError(int)} with {@link #ERROR_INVALID_INTERVAL}.
|
||||||
|
* @param flags Flags to enable/disable available options on this keepalive.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void startImpl(int intervalSec) {
|
protected void startImpl(int intervalSec, int flags) {
|
||||||
|
if (0 != (flags & ~FLAG_AUTOMATIC_ON_OFF)) {
|
||||||
|
throw new IllegalArgumentException("Illegal flag value for "
|
||||||
|
+ this.getClass().getSimpleName() + " : " + flags);
|
||||||
|
}
|
||||||
|
final boolean automaticOnOffKeepalives = 0 != (flags & FLAG_AUTOMATIC_ON_OFF);
|
||||||
mExecutor.execute(() -> {
|
mExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
mService.startNattKeepaliveWithFd(mNetwork, mPfd, mResourceId,
|
mService.startNattKeepaliveWithFd(mNetwork, mPfd, mResourceId,
|
||||||
intervalSec, mCallback,
|
intervalSec, mCallback, mSource.getHostAddress(),
|
||||||
mSource.getHostAddress(), mDestination.getHostAddress());
|
mDestination.getHostAddress(), automaticOnOffKeepalives);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Error starting socket keepalive: ", e);
|
Log.e(TAG, "Error starting socket keepalive: ", e);
|
||||||
throw e.rethrowFromSystemServer();
|
throw e.rethrowFromSystemServer();
|
||||||
|
|||||||
@@ -483,6 +483,20 @@ public abstract class NetworkAgent {
|
|||||||
*/
|
*/
|
||||||
public static final int EVENT_UNREGISTER_AFTER_REPLACEMENT = BASE + 29;
|
public static final int EVENT_UNREGISTER_AFTER_REPLACEMENT = BASE + 29;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent by AutomaticOnOffKeepaliveTracker periodically (when relevant) to trigger monitor
|
||||||
|
* automatic keepalive request.
|
||||||
|
*
|
||||||
|
* NATT keepalives have an automatic mode where the system only sends keepalive packets when
|
||||||
|
* TCP sockets are open over a VPN. The system will check periodically for presence of
|
||||||
|
* such open sockets, and this message is what triggers the re-evaluation.
|
||||||
|
*
|
||||||
|
* arg1 = hardware slot number of the keepalive
|
||||||
|
* obj = {@link Network} that the keepalive is started on.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final int CMD_MONITOR_AUTOMATIC_KEEPALIVE = BASE + 30;
|
||||||
|
|
||||||
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
|
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
|
||||||
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
|
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
|
||||||
config.legacyTypeName, config.legacySubTypeName);
|
config.legacyTypeName, config.legacySubTypeName);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
|
import static android.annotation.SystemApi.Client.PRIVILEGED_APPS;
|
||||||
|
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
import android.annotation.IntRange;
|
import android.annotation.IntRange;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
@@ -173,6 +175,27 @@ public abstract class SocketKeepalive implements AutoCloseable {
|
|||||||
})
|
})
|
||||||
public @interface KeepaliveEvent {}
|
public @interface KeepaliveEvent {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the system automatically toggles keepalive when no TCP connection is open on the VPN.
|
||||||
|
*
|
||||||
|
* If this flag is present, the system will monitor the VPN(s) running on top of the specified
|
||||||
|
* network for open TCP connections. When no such connections are open, it will turn off the
|
||||||
|
* keepalives to conserve battery power. When there is at least one such connection it will
|
||||||
|
* turn on the keepalives to make sure functionality is preserved.
|
||||||
|
*
|
||||||
|
* This only works with {@link NattSocketKeepalive}.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@SystemApi
|
||||||
|
public static final int FLAG_AUTOMATIC_ON_OFF = 1 << 0;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef(prefix = { "FLAG_"}, flag = true, value = {
|
||||||
|
FLAG_AUTOMATIC_ON_OFF
|
||||||
|
})
|
||||||
|
public @interface StartFlags {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The minimum interval in seconds between keepalive packet transmissions.
|
* The minimum interval in seconds between keepalive packet transmissions.
|
||||||
*
|
*
|
||||||
@@ -294,13 +317,15 @@ public abstract class SocketKeepalive implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that keepalive be started with the given {@code intervalSec}. See
|
* Request that keepalive be started with the given {@code intervalSec}.
|
||||||
* {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an exception
|
*
|
||||||
* when invoking start or stop of the {@link SocketKeepalive}, a {@link RemoteException} will be
|
* See {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an
|
||||||
* thrown into the {@code executor}. This is typically not important to catch because the remote
|
* exception when invoking start or stop of the {@link SocketKeepalive}, a
|
||||||
* party is the system, so if it is not in shape to communicate through binder the system is
|
* {@link RuntimeException} caused by a {@link RemoteException} will be thrown into the
|
||||||
* probably going down anyway. If the caller cares regardless, it can use a custom
|
* {@link Executor}. This is typically not important to catch because the remote party is
|
||||||
* {@link Executor} to catch the {@link RemoteException}.
|
* the system, so if it is not in shape to communicate through binder the system is going
|
||||||
|
* down anyway. If the caller still cares, it can use a custom {@link Executor} to catch the
|
||||||
|
* {@link RuntimeException}.
|
||||||
*
|
*
|
||||||
* @param intervalSec The target interval in seconds between keepalive packet transmissions.
|
* @param intervalSec The target interval in seconds between keepalive packet transmissions.
|
||||||
* The interval should be between 10 seconds and 3600 seconds, otherwise
|
* The interval should be between 10 seconds and 3600 seconds, otherwise
|
||||||
@@ -308,11 +333,35 @@ public abstract class SocketKeepalive implements AutoCloseable {
|
|||||||
*/
|
*/
|
||||||
public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
|
public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
|
||||||
int intervalSec) {
|
int intervalSec) {
|
||||||
startImpl(intervalSec);
|
startImpl(intervalSec, 0 /* flags */);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request that keepalive be started with the given {@code intervalSec}.
|
||||||
|
*
|
||||||
|
* See {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an
|
||||||
|
* exception when invoking start or stop of the {@link SocketKeepalive}, a
|
||||||
|
* {@link RuntimeException} caused by a {@link RemoteException} will be thrown into the
|
||||||
|
* {@link Executor}. This is typically not important to catch because the remote party is
|
||||||
|
* the system, so if it is not in shape to communicate through binder the system is going
|
||||||
|
* down anyway. If the caller still cares, it can use a custom {@link Executor} to catch the
|
||||||
|
* {@link RuntimeException}.
|
||||||
|
*
|
||||||
|
* @param intervalSec The target interval in seconds between keepalive packet transmissions.
|
||||||
|
* The interval should be between 10 seconds and 3600 seconds. Otherwise,
|
||||||
|
* the supplied {@link Callback} will see a call to
|
||||||
|
* {@link Callback#onError(int)} with {@link #ERROR_INVALID_INTERVAL}.
|
||||||
|
* @param flags Flags to enable/disable available options on this keepalive.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@SystemApi(client = PRIVILEGED_APPS)
|
||||||
|
public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
|
||||||
|
int intervalSec, @StartFlags int flags) {
|
||||||
|
startImpl(intervalSec, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
protected abstract void startImpl(int intervalSec);
|
protected abstract void startImpl(int intervalSec, @StartFlags int flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
|
* Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
|
||||||
|
|||||||
@@ -50,7 +50,11 @@ public final class TcpSocketKeepalive extends SocketKeepalive {
|
|||||||
* acknowledgement.
|
* acknowledgement.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void startImpl(int intervalSec) {
|
protected void startImpl(int intervalSec, int flags) {
|
||||||
|
if (0 != flags) {
|
||||||
|
throw new IllegalArgumentException("Illegal flag value for "
|
||||||
|
+ this.getClass().getSimpleName() + " : " + flags);
|
||||||
|
}
|
||||||
mExecutor.execute(() -> {
|
mExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
mService.startTcpKeepalive(mNetwork, mPfd, intervalSec, mCallback);
|
mService.startTcpKeepalive(mNetwork, mPfd, intervalSec, mCallback);
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ import static com.android.net.module.util.NetworkMonitorUtils.isPrivateDnsValida
|
|||||||
import static com.android.net.module.util.PermissionUtils.enforceAnyPermissionOf;
|
import static com.android.net.module.util.PermissionUtils.enforceAnyPermissionOf;
|
||||||
import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermission;
|
import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermission;
|
||||||
import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermissionOr;
|
import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermissionOr;
|
||||||
import static com.android.server.connectivity.KeepaliveTracker.PERMISSION;
|
|
||||||
|
|
||||||
import static java.util.Map.Entry;
|
import static java.util.Map.Entry;
|
||||||
|
|
||||||
@@ -278,6 +277,7 @@ import com.android.server.connectivity.DnsManager;
|
|||||||
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
|
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
|
||||||
import com.android.server.connectivity.DscpPolicyTracker;
|
import com.android.server.connectivity.DscpPolicyTracker;
|
||||||
import com.android.server.connectivity.FullScore;
|
import com.android.server.connectivity.FullScore;
|
||||||
|
import com.android.server.connectivity.KeepaliveTracker;
|
||||||
import com.android.server.connectivity.LingerMonitor;
|
import com.android.server.connectivity.LingerMonitor;
|
||||||
import com.android.server.connectivity.MockableSystemProperties;
|
import com.android.server.connectivity.MockableSystemProperties;
|
||||||
import com.android.server.connectivity.MultinetworkPolicyTracker;
|
import com.android.server.connectivity.MultinetworkPolicyTracker;
|
||||||
@@ -2999,7 +2999,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void enforceKeepalivePermission() {
|
private void enforceKeepalivePermission() {
|
||||||
mContext.enforceCallingOrSelfPermission(PERMISSION, "ConnectivityService");
|
mContext.enforceCallingOrSelfPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkLocalMacAddressPermission(int pid, int uid) {
|
private boolean checkLocalMacAddressPermission(int pid, int uid) {
|
||||||
@@ -5545,6 +5545,33 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
mKeepaliveTracker.handleStartKeepalive(msg);
|
mKeepaliveTracker.handleStartKeepalive(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE: {
|
||||||
|
final Network network = (Network) msg.obj;
|
||||||
|
final int slot = msg.arg1;
|
||||||
|
|
||||||
|
boolean networkFound = false;
|
||||||
|
final ArrayList<NetworkAgentInfo> vpnsRunningOnThisNetwork = new ArrayList<>();
|
||||||
|
for (NetworkAgentInfo n : mNetworkAgentInfos) {
|
||||||
|
if (n.network.equals(network)) networkFound = true;
|
||||||
|
if (n.isVPN() && n.everConnected() && hasUnderlyingNetwork(n, network)) {
|
||||||
|
vpnsRunningOnThisNetwork.add(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the network no longer exists, then the keepalive should have been
|
||||||
|
// cleaned up already. There is no point trying to resume keepalives.
|
||||||
|
if (!networkFound) return;
|
||||||
|
|
||||||
|
if (!vpnsRunningOnThisNetwork.isEmpty()) {
|
||||||
|
mKeepaliveTracker.handleMonitorAutomaticKeepalive(network, slot,
|
||||||
|
// TODO: check all the VPNs running on top of this network
|
||||||
|
vpnsRunningOnThisNetwork.get(0).network.netId);
|
||||||
|
} else {
|
||||||
|
// If no VPN, then make sure the keepalive is running.
|
||||||
|
mKeepaliveTracker.handleMaybeResumeKeepalive(network, slot);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
// Sent by KeepaliveTracker to process an app request on the state machine thread.
|
// Sent by KeepaliveTracker to process an app request on the state machine thread.
|
||||||
case NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE: {
|
case NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE: {
|
||||||
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork((Network) msg.obj);
|
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork((Network) msg.obj);
|
||||||
@@ -9789,20 +9816,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
enforceKeepalivePermission();
|
enforceKeepalivePermission();
|
||||||
mKeepaliveTracker.startNattKeepalive(
|
mKeepaliveTracker.startNattKeepalive(
|
||||||
getNetworkAgentInfoForNetwork(network), null /* fd */,
|
getNetworkAgentInfoForNetwork(network), null /* fd */,
|
||||||
intervalSeconds, cb,
|
intervalSeconds, cb, srcAddr, srcPort, dstAddr, NattSocketKeepalive.NATT_PORT,
|
||||||
srcAddr, srcPort, dstAddr, NattSocketKeepalive.NATT_PORT);
|
// Keep behavior of the deprecated method as it is. Set automaticOnOffKeepalives to
|
||||||
|
// false because there is no way and no plan to configure automaticOnOffKeepalives
|
||||||
|
// in this deprecated method.
|
||||||
|
false /* automaticOnOffKeepalives */);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startNattKeepaliveWithFd(Network network, ParcelFileDescriptor pfd, int resourceId,
|
public void startNattKeepaliveWithFd(Network network, ParcelFileDescriptor pfd, int resourceId,
|
||||||
int intervalSeconds, ISocketKeepaliveCallback cb, String srcAddr,
|
int intervalSeconds, ISocketKeepaliveCallback cb, String srcAddr,
|
||||||
String dstAddr) {
|
String dstAddr, boolean automaticOnOffKeepalives) {
|
||||||
try {
|
try {
|
||||||
final FileDescriptor fd = pfd.getFileDescriptor();
|
final FileDescriptor fd = pfd.getFileDescriptor();
|
||||||
mKeepaliveTracker.startNattKeepalive(
|
mKeepaliveTracker.startNattKeepalive(
|
||||||
getNetworkAgentInfoForNetwork(network), fd, resourceId,
|
getNetworkAgentInfoForNetwork(network), fd, resourceId,
|
||||||
intervalSeconds, cb,
|
intervalSeconds, cb,
|
||||||
srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT);
|
srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT, automaticOnOffKeepalives);
|
||||||
} finally {
|
} finally {
|
||||||
// FileDescriptors coming from AIDL calls must be manually closed to prevent leaks.
|
// FileDescriptors coming from AIDL calls must be manually closed to prevent leaks.
|
||||||
// startNattKeepalive calls Os.dup(fd) before returning, so we can close immediately.
|
// startNattKeepalive calls Os.dup(fd) before returning, so we can close immediately.
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package com.android.server.connectivity;
|
package com.android.server.connectivity;
|
||||||
|
|
||||||
|
import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
|
||||||
|
import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
|
||||||
|
import static android.net.SocketKeepalive.SUCCESS;
|
||||||
import static android.system.OsConstants.AF_INET;
|
import static android.system.OsConstants.AF_INET;
|
||||||
import static android.system.OsConstants.AF_INET6;
|
import static android.system.OsConstants.AF_INET6;
|
||||||
import static android.system.OsConstants.SOL_SOCKET;
|
import static android.system.OsConstants.SOL_SOCKET;
|
||||||
@@ -26,16 +29,28 @@ import static com.android.net.module.util.netlink.NetlinkConstants.SOCKDIAG_MSG_
|
|||||||
import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
|
import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
|
||||||
import static com.android.net.module.util.netlink.NetlinkUtils.IO_TIMEOUT_MS;
|
import static com.android.net.module.util.netlink.NetlinkUtils.IO_TIMEOUT_MS;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
import android.net.ISocketKeepaliveCallback;
|
import android.net.ISocketKeepaliveCallback;
|
||||||
import android.net.MarkMaskParcel;
|
import android.net.MarkMaskParcel;
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.NetworkAgent;
|
||||||
|
import android.net.SocketKeepalive;
|
||||||
|
import android.net.SocketKeepalive.InvalidSocketException;
|
||||||
|
import android.os.FileUtils;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import android.system.StructTimeval;
|
import android.system.StructTimeval;
|
||||||
@@ -44,6 +59,7 @@ import android.util.SparseArray;
|
|||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
|
import com.android.modules.utils.build.SdkLevel;
|
||||||
import com.android.net.module.util.HexDump;
|
import com.android.net.module.util.HexDump;
|
||||||
import com.android.net.module.util.SocketUtils;
|
import com.android.net.module.util.SocketUtils;
|
||||||
import com.android.net.module.util.netlink.InetDiagMessage;
|
import com.android.net.module.util.netlink.InetDiagMessage;
|
||||||
@@ -52,23 +68,57 @@ import com.android.net.module.util.netlink.StructNlAttr;
|
|||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.nio.BufferUnderflowException;
|
import java.nio.BufferUnderflowException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages automatic on/off socket keepalive requests.
|
* Manages automatic on/off socket keepalive requests.
|
||||||
*
|
*
|
||||||
* Provides methods to stop and start automatic keepalive requests, and keeps track of keepalives
|
* Provides methods to stop and start automatic keepalive requests, and keeps track of keepalives
|
||||||
* across all networks. For non-automatic on/off keepalive request, this class bypass the requests
|
* across all networks. For non-automatic on/off keepalive request, this class just forwards the
|
||||||
* and send to KeepaliveTrakcer. This class is tightly coupled to ConnectivityService. It is not
|
* requests to KeepaliveTracker. This class is tightly coupled to ConnectivityService. It is not
|
||||||
* thread-safe and its handle* methods must be called only from the ConnectivityService handler
|
* thread-safe and its handle* methods must be called only from the ConnectivityService handler
|
||||||
* thread.
|
* thread.
|
||||||
*/
|
*/
|
||||||
public class AutomaticOnOffKeepaliveTracker {
|
public class AutomaticOnOffKeepaliveTracker {
|
||||||
private static final String TAG = "AutomaticOnOffKeepaliveTracker";
|
private static final String TAG = "AutomaticOnOffKeepaliveTracker";
|
||||||
private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET};
|
private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET};
|
||||||
|
private static final String ACTION_TCP_POLLING_ALARM =
|
||||||
|
"com.android.server.connectivity.KeepaliveTracker.TCP_POLLING_ALARM";
|
||||||
|
private static final String EXTRA_NETWORK = "network_id";
|
||||||
|
private static final String EXTRA_SLOT = "slot";
|
||||||
|
private static final long DEFAULT_TCP_POLLING_INTERVAL_MS = 120_000L;
|
||||||
|
/**
|
||||||
|
* States for {@code #AutomaticOnOffKeepalive}.
|
||||||
|
*
|
||||||
|
* A new AutomaticOnOffKeepalive starts with STATE_ENABLED. The system will monitor
|
||||||
|
* the TCP sockets on VPN networks running on top of the specified network, and turn off
|
||||||
|
* keepalive if there is no TCP socket any of the VPN networks. Conversely, it will turn
|
||||||
|
* keepalive back on if any TCP socket is open on any of the VPN networks.
|
||||||
|
*
|
||||||
|
* When there is no TCP socket on any of the VPN networks, the state becomes STATE_SUSPENDED.
|
||||||
|
* The {@link KeepaliveTracker.KeepaliveInfo} object is kept to remember the parameters so it
|
||||||
|
* is possible to resume keepalive later with the same parameters.
|
||||||
|
*
|
||||||
|
* When the system detects some TCP socket is open on one of the VPNs while in STATE_SUSPENDED,
|
||||||
|
* this AutomaticOnOffKeepalive goes to STATE_ENABLED again.
|
||||||
|
*
|
||||||
|
* When finishing keepalive, this object is deleted.
|
||||||
|
*/
|
||||||
|
private static final int STATE_ENABLED = 0;
|
||||||
|
private static final int STATE_SUSPENDED = 1;
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef(prefix = { "STATE_" }, value = {
|
||||||
|
STATE_ENABLED,
|
||||||
|
STATE_SUSPENDED
|
||||||
|
})
|
||||||
|
private @interface AutomaticOnOffState {}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Handler mConnectivityServiceHandler;
|
private final Handler mConnectivityServiceHandler;
|
||||||
@@ -76,6 +126,8 @@ public class AutomaticOnOffKeepaliveTracker {
|
|||||||
private final KeepaliveTracker mKeepaliveTracker;
|
private final KeepaliveTracker mKeepaliveTracker;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
@NonNull
|
||||||
|
private final AlarmManager mAlarmManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@code inetDiagReqV2} messages for different IP family.
|
* The {@code inetDiagReqV2} messages for different IP family.
|
||||||
@@ -88,8 +140,73 @@ public class AutomaticOnOffKeepaliveTracker {
|
|||||||
private final SparseArray<byte[]> mSockDiagMsg = new SparseArray<>();
|
private final SparseArray<byte[]> mSockDiagMsg = new SparseArray<>();
|
||||||
private final Dependencies mDependencies;
|
private final Dependencies mDependencies;
|
||||||
private final INetd mNetd;
|
private final INetd mNetd;
|
||||||
|
/**
|
||||||
|
* Keeps track of automatic on/off keepalive requests.
|
||||||
|
* This should be only updated in ConnectivityService handler thread.
|
||||||
|
*/
|
||||||
|
private final ArrayList<AutomaticOnOffKeepalive> mAutomaticOnOffKeepalives = new ArrayList<>();
|
||||||
|
|
||||||
public AutomaticOnOffKeepaliveTracker(Context context, Handler handler) {
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (ACTION_TCP_POLLING_ALARM.equals(intent.getAction())) {
|
||||||
|
Log.d(TAG, "Received TCP polling intent");
|
||||||
|
final Network network = intent.getParcelableExtra(EXTRA_NETWORK);
|
||||||
|
final int slot = intent.getIntExtra(EXTRA_SLOT, -1);
|
||||||
|
mConnectivityServiceHandler.obtainMessage(
|
||||||
|
NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE,
|
||||||
|
slot, 0 , network).sendToTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static class AutomaticOnOffKeepalive {
|
||||||
|
@NonNull
|
||||||
|
private final KeepaliveTracker.KeepaliveInfo mKi;
|
||||||
|
@NonNull
|
||||||
|
private final FileDescriptor mFd;
|
||||||
|
@NonNull
|
||||||
|
private final PendingIntent mTcpPollingAlarm;
|
||||||
|
private final int mSlot;
|
||||||
|
@AutomaticOnOffState
|
||||||
|
private int mAutomaticOnOffState = STATE_ENABLED;
|
||||||
|
|
||||||
|
AutomaticOnOffKeepalive(@NonNull KeepaliveTracker.KeepaliveInfo ki,
|
||||||
|
@NonNull Context context) throws InvalidSocketException {
|
||||||
|
this.mKi = Objects.requireNonNull(ki);
|
||||||
|
// A null fd is acceptable in KeepaliveInfo for backward compatibility of
|
||||||
|
// PacketKeepalive API, but it should not happen here because legacy API cannot setup
|
||||||
|
// automatic keepalive.
|
||||||
|
Objects.requireNonNull(ki.mFd);
|
||||||
|
|
||||||
|
// Get the slot from keepalive because the slot information may be missing when the
|
||||||
|
// keepalive is stopped.
|
||||||
|
this.mSlot = ki.getSlot();
|
||||||
|
try {
|
||||||
|
this.mFd = Os.dup(ki.mFd);
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
Log.e(TAG, "Cannot dup fd: ", e);
|
||||||
|
throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
|
||||||
|
}
|
||||||
|
mTcpPollingAlarm = createTcpPollingAlarmIntent(
|
||||||
|
context, ki.getNai().network(), ki.getSlot());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean match(Network network, int slot) {
|
||||||
|
return this.mKi.getNai().network().equals(network) && this.mSlot == slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingIntent createTcpPollingAlarmIntent(@NonNull Context context,
|
||||||
|
@NonNull Network network, int slot) {
|
||||||
|
final Intent intent = new Intent(ACTION_TCP_POLLING_ALARM);
|
||||||
|
intent.putExtra(EXTRA_NETWORK, network);
|
||||||
|
intent.putExtra(EXTRA_SLOT, slot);
|
||||||
|
return PendingIntent.getBroadcast(
|
||||||
|
context, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AutomaticOnOffKeepaliveTracker(@NonNull Context context, @NonNull Handler handler) {
|
||||||
this(context, handler, new Dependencies(context));
|
this(context, handler, new Dependencies(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,15 +214,111 @@ public class AutomaticOnOffKeepaliveTracker {
|
|||||||
public AutomaticOnOffKeepaliveTracker(@NonNull Context context, @NonNull Handler handler,
|
public AutomaticOnOffKeepaliveTracker(@NonNull Context context, @NonNull Handler handler,
|
||||||
@NonNull Dependencies dependencies) {
|
@NonNull Dependencies dependencies) {
|
||||||
mContext = Objects.requireNonNull(context);
|
mContext = Objects.requireNonNull(context);
|
||||||
mDependencies = dependencies;
|
mDependencies = Objects.requireNonNull(dependencies);
|
||||||
this.mConnectivityServiceHandler = Objects.requireNonNull(handler);
|
mConnectivityServiceHandler = Objects.requireNonNull(handler);
|
||||||
mNetd = mDependencies.getNetd();
|
mNetd = mDependencies.getNetd();
|
||||||
mKeepaliveTracker = mDependencies.newKeepaliveTracker(
|
mKeepaliveTracker = mDependencies.newKeepaliveTracker(
|
||||||
mContext, mConnectivityServiceHandler);
|
mContext, mConnectivityServiceHandler);
|
||||||
|
|
||||||
|
if (SdkLevel.isAtLeastU()) {
|
||||||
|
mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_TCP_POLLING_ALARM),
|
||||||
|
null, handler);
|
||||||
|
}
|
||||||
|
mAlarmManager = mContext.getSystemService(AlarmManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startTcpPollingAlarm(@NonNull PendingIntent alarm) {
|
||||||
|
final long triggerAtMillis =
|
||||||
|
SystemClock.elapsedRealtime() + DEFAULT_TCP_POLLING_INTERVAL_MS;
|
||||||
|
// Setup a non-wake up alarm.
|
||||||
|
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, triggerAtMillis, alarm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if any state transition is needed for the specific automatic keepalive.
|
||||||
|
*/
|
||||||
|
public void handleMonitorAutomaticKeepalive(@NonNull Network network, int slot, int vpnNetId) {
|
||||||
|
final AutomaticOnOffKeepalive autoKi = findAutomaticOnOffKeepalive(network, slot);
|
||||||
|
// This may happen if the keepalive is removed by the app, and the alarm is fired at the
|
||||||
|
// same time.
|
||||||
|
if (autoKi == null) return;
|
||||||
|
|
||||||
|
handleMonitorTcpConnections(autoKi, vpnNetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if disable or re-enable keepalive is needed or not based on TCP sockets status.
|
||||||
|
*/
|
||||||
|
private void handleMonitorTcpConnections(@NonNull AutomaticOnOffKeepalive ki, int vpnNetId) {
|
||||||
|
if (!isAnyTcpSocketConnected(vpnNetId)) {
|
||||||
|
// No TCP socket exists. Stop keepalive if ENABLED, and remain SUSPENDED if currently
|
||||||
|
// SUSPENDED.
|
||||||
|
if (ki.mAutomaticOnOffState == STATE_ENABLED) {
|
||||||
|
ki.mAutomaticOnOffState = STATE_SUSPENDED;
|
||||||
|
handleSuspendKeepalive(ki.mKi.mNai, ki.mSlot, SUCCESS);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleMaybeResumeKeepalive(ki);
|
||||||
|
}
|
||||||
|
// TODO: listen to socket status instead of periodically check.
|
||||||
|
startTcpPollingAlarm(ki.mTcpPollingAlarm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resume keepalive for this slot on this network, if it wasn't already resumed.
|
||||||
|
*/
|
||||||
|
public void handleMaybeResumeKeepalive(@NonNull final Network network, final int slot) {
|
||||||
|
final AutomaticOnOffKeepalive autoKi = findAutomaticOnOffKeepalive(network, slot);
|
||||||
|
// This may happen if the keepalive is removed by the app, and the alarm is fired at
|
||||||
|
// the same time.
|
||||||
|
if (autoKi == null) return;
|
||||||
|
handleMaybeResumeKeepalive(autoKi);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleMaybeResumeKeepalive(@NonNull AutomaticOnOffKeepalive autoKi) {
|
||||||
|
if (autoKi.mAutomaticOnOffState == STATE_ENABLED) return;
|
||||||
|
KeepaliveTracker.KeepaliveInfo newKi;
|
||||||
|
try {
|
||||||
|
// Get fd from AutomaticOnOffKeepalive since the fd in the original
|
||||||
|
// KeepaliveInfo should be closed.
|
||||||
|
newKi = autoKi.mKi.withFd(autoKi.mFd);
|
||||||
|
} catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
|
||||||
|
Log.e(TAG, "Fail to construct keepalive", e);
|
||||||
|
mKeepaliveTracker.notifyErrorCallback(autoKi.mKi.mCallback, ERROR_INVALID_SOCKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
autoKi.mAutomaticOnOffState = STATE_ENABLED;
|
||||||
|
handleResumeKeepalive(mConnectivityServiceHandler.obtainMessage(
|
||||||
|
NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
|
||||||
|
autoKi.mAutomaticOnOffState, 0, newKi));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findAutomaticOnOffKeepaliveIndex(@NonNull Network network, int slot) {
|
||||||
|
ensureRunningOnHandlerThread();
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (AutomaticOnOffKeepalive ki : mAutomaticOnOffKeepalives) {
|
||||||
|
if (ki.match(network, slot)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private AutomaticOnOffKeepalive findAutomaticOnOffKeepalive(@NonNull Network network,
|
||||||
|
int slot) {
|
||||||
|
ensureRunningOnHandlerThread();
|
||||||
|
|
||||||
|
final int index = findAutomaticOnOffKeepaliveIndex(network, slot);
|
||||||
|
return (index >= 0) ? mAutomaticOnOffKeepalives.get(index) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle keepalive events from lower layer.
|
* Handle keepalive events from lower layer.
|
||||||
|
*
|
||||||
|
* Forward to KeepaliveTracker.
|
||||||
*/
|
*/
|
||||||
public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai, int slot, int reason) {
|
public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai, int slot, int reason) {
|
||||||
mKeepaliveTracker.handleEventSocketKeepalive(nai, slot, reason);
|
mKeepaliveTracker.handleEventSocketKeepalive(nai, slot, reason);
|
||||||
@@ -116,27 +329,86 @@ public class AutomaticOnOffKeepaliveTracker {
|
|||||||
*/
|
*/
|
||||||
public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
|
public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
|
||||||
mKeepaliveTracker.handleStopAllKeepalives(nai, reason);
|
mKeepaliveTracker.handleStopAllKeepalives(nai, reason);
|
||||||
|
final Iterator<AutomaticOnOffKeepalive> iterator = mAutomaticOnOffKeepalives.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
final AutomaticOnOffKeepalive autoKi = iterator.next();
|
||||||
|
if (autoKi.mKi.getNai() == nai) {
|
||||||
|
cleanupAutoOnOffKeepalive(autoKi);
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle start keepalives with the message.
|
* Handle start keepalive contained within a message.
|
||||||
*
|
*
|
||||||
* The message is expected to be a KeepaliveTracker.KeepaliveInfo.
|
* The message is expected to contain a KeepaliveTracker.KeepaliveInfo.
|
||||||
*/
|
*/
|
||||||
public void handleStartKeepalive(Message message) {
|
public void handleStartKeepalive(Message message) {
|
||||||
mKeepaliveTracker.handleStartKeepalive(message);
|
mKeepaliveTracker.handleStartKeepalive(message);
|
||||||
|
|
||||||
|
// Add automatic on/off request into list to track its life cycle.
|
||||||
|
final boolean automaticOnOff = message.arg1 != 0;
|
||||||
|
if (automaticOnOff) {
|
||||||
|
final KeepaliveTracker.KeepaliveInfo ki = (KeepaliveTracker.KeepaliveInfo) message.obj;
|
||||||
|
AutomaticOnOffKeepalive autoKi;
|
||||||
|
try {
|
||||||
|
// CAREFUL : mKeepaliveTracker.handleStartKeepalive will assign |ki.mSlot| after
|
||||||
|
// pulling |ki| from the message. The constructor below will read this member
|
||||||
|
// (through ki.getSlot()) and therefore actively relies on handleStartKeepalive
|
||||||
|
// having assigned this member before this is called.
|
||||||
|
// TODO : clean this up by assigning the slot at the start of this method instead
|
||||||
|
// and ideally removing the mSlot member from KeepaliveInfo.
|
||||||
|
autoKi = new AutomaticOnOffKeepalive(ki, mContext);
|
||||||
|
} catch (SocketKeepalive.InvalidSocketException | IllegalArgumentException e) {
|
||||||
|
Log.e(TAG, "Fail to construct keepalive", e);
|
||||||
|
mKeepaliveTracker.notifyErrorCallback(ki.mCallback, ERROR_INVALID_SOCKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mAutomaticOnOffKeepalives.add(autoKi);
|
||||||
|
startTcpPollingAlarm(autoKi.mTcpPollingAlarm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleResumeKeepalive(Message message) {
|
||||||
|
mKeepaliveTracker.handleStartKeepalive(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSuspendKeepalive(NetworkAgentInfo nai, int slot, int reason) {
|
||||||
|
mKeepaliveTracker.handleStopKeepalive(nai, slot, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle stop keepalives on the specific network with given slot.
|
* Handle stop keepalives on the specific network with given slot.
|
||||||
*/
|
*/
|
||||||
public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
|
public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
|
||||||
mKeepaliveTracker.handleStopKeepalive(nai, slot, reason);
|
final AutomaticOnOffKeepalive autoKi = findAutomaticOnOffKeepalive(nai.network, slot);
|
||||||
|
|
||||||
|
// Let the original keepalive do the stop first, and then clean up the keepalive if it's an
|
||||||
|
// automatic keepalive.
|
||||||
|
if (autoKi == null || autoKi.mAutomaticOnOffState == STATE_ENABLED) {
|
||||||
|
mKeepaliveTracker.handleStopKeepalive(nai, slot, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not an AutomaticOnOffKeepalive.
|
||||||
|
if (autoKi == null) return;
|
||||||
|
|
||||||
|
cleanupAutoOnOffKeepalive(autoKi);
|
||||||
|
mAutomaticOnOffKeepalives.remove(autoKi);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanupAutoOnOffKeepalive(@NonNull final AutomaticOnOffKeepalive autoKi) {
|
||||||
|
ensureRunningOnHandlerThread();
|
||||||
|
mAlarmManager.cancel(autoKi.mTcpPollingAlarm);
|
||||||
|
// Close the duplicated fd that maintains the lifecycle of socket.
|
||||||
|
FileUtils.closeQuietly(autoKi.mFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when requesting that keepalives be started on a IPsec NAT-T socket. See
|
* Called when requesting that keepalives be started on a IPsec NAT-T socket. See
|
||||||
* {@link android.net.SocketKeepalive}.
|
* {@link android.net.SocketKeepalive}.
|
||||||
|
*
|
||||||
|
* Forward to KeepaliveTracker.
|
||||||
**/
|
**/
|
||||||
public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
|
public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
|
||||||
@Nullable FileDescriptor fd,
|
@Nullable FileDescriptor fd,
|
||||||
@@ -145,14 +417,21 @@ public class AutomaticOnOffKeepaliveTracker {
|
|||||||
@NonNull String srcAddrString,
|
@NonNull String srcAddrString,
|
||||||
int srcPort,
|
int srcPort,
|
||||||
@NonNull String dstAddrString,
|
@NonNull String dstAddrString,
|
||||||
int dstPort) {
|
int dstPort, boolean automaticOnOffKeepalives) {
|
||||||
mKeepaliveTracker.startNattKeepalive(nai, fd, intervalSeconds, cb, srcAddrString,
|
final KeepaliveTracker.KeepaliveInfo ki = mKeepaliveTracker.makeNattKeepaliveInfo(nai, fd,
|
||||||
srcPort, dstAddrString, dstPort);
|
intervalSeconds, cb, srcAddrString, srcPort, dstAddrString, dstPort);
|
||||||
|
if (null != ki) {
|
||||||
|
mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
|
||||||
|
// TODO : move ConnectivityService#encodeBool to a static lib.
|
||||||
|
automaticOnOffKeepalives ? 1 : 0, 0, ki).sendToTarget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when requesting that keepalives be started on a IPsec NAT-T socket. See
|
* Called when requesting that keepalives be started on a IPsec NAT-T socket. See
|
||||||
* {@link android.net.SocketKeepalive}.
|
* {@link android.net.SocketKeepalive}.
|
||||||
|
*
|
||||||
|
* Forward to KeepaliveTracker.
|
||||||
**/
|
**/
|
||||||
public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
|
public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
|
||||||
@Nullable FileDescriptor fd,
|
@Nullable FileDescriptor fd,
|
||||||
@@ -161,9 +440,15 @@ public class AutomaticOnOffKeepaliveTracker {
|
|||||||
@NonNull ISocketKeepaliveCallback cb,
|
@NonNull ISocketKeepaliveCallback cb,
|
||||||
@NonNull String srcAddrString,
|
@NonNull String srcAddrString,
|
||||||
@NonNull String dstAddrString,
|
@NonNull String dstAddrString,
|
||||||
int dstPort) {
|
int dstPort,
|
||||||
mKeepaliveTracker.startNattKeepalive(nai, fd, resourceId, intervalSeconds, cb,
|
boolean automaticOnOffKeepalives) {
|
||||||
srcAddrString, dstAddrString, dstPort);
|
final KeepaliveTracker.KeepaliveInfo ki = mKeepaliveTracker.makeNattKeepaliveInfo(nai, fd,
|
||||||
|
resourceId, intervalSeconds, cb, srcAddrString, dstAddrString, dstPort);
|
||||||
|
if (null != ki) {
|
||||||
|
mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
|
||||||
|
// TODO : move ConnectivityService#encodeBool to a static lib.
|
||||||
|
automaticOnOffKeepalives ? 1 : 0, 0, ki).sendToTarget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -173,26 +458,34 @@ public class AutomaticOnOffKeepaliveTracker {
|
|||||||
* other fields are needed to form the keepalive packet. Thus, this function synchronously
|
* other fields are needed to form the keepalive packet. Thus, this function synchronously
|
||||||
* puts the socket into repair mode to get the necessary information. After the socket has been
|
* puts the socket into repair mode to get the necessary information. After the socket has been
|
||||||
* put into repair mode, the application cannot access the socket until reverted to normal.
|
* put into repair mode, the application cannot access the socket until reverted to normal.
|
||||||
*
|
|
||||||
* See {@link android.net.SocketKeepalive}.
|
* See {@link android.net.SocketKeepalive}.
|
||||||
|
*
|
||||||
|
* Forward to KeepaliveTracker.
|
||||||
**/
|
**/
|
||||||
public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
|
public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
|
||||||
@NonNull FileDescriptor fd,
|
@NonNull FileDescriptor fd,
|
||||||
int intervalSeconds,
|
int intervalSeconds,
|
||||||
@NonNull ISocketKeepaliveCallback cb) {
|
@NonNull ISocketKeepaliveCallback cb) {
|
||||||
mKeepaliveTracker.startTcpKeepalive(nai, fd, intervalSeconds, cb);
|
final KeepaliveTracker.KeepaliveInfo ki = mKeepaliveTracker.makeTcpKeepaliveInfo(nai, fd,
|
||||||
|
intervalSeconds, cb);
|
||||||
|
if (null != ki) {
|
||||||
|
mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki)
|
||||||
|
.sendToTarget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump AutomaticOnOffKeepaliveTracker state.
|
* Dump AutomaticOnOffKeepaliveTracker state.
|
||||||
*/
|
*/
|
||||||
public void dump(IndentingPrintWriter pw) {
|
public void dump(IndentingPrintWriter pw) {
|
||||||
// TODO: Dump the necessary information for automatic on/off keepalive.
|
// TODO: Dump the necessary information for automatic on/off keepalive.
|
||||||
mKeepaliveTracker.dump(pw);
|
mKeepaliveTracker.dump(pw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check all keeplaives on the network are still valid.
|
* Check all keepalives on the network are still valid.
|
||||||
|
*
|
||||||
|
* Forward to KeepaliveTracker.
|
||||||
*/
|
*/
|
||||||
public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) {
|
public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) {
|
||||||
mKeepaliveTracker.handleCheckKeepalivesStillValid(nai);
|
mKeepaliveTracker.handleCheckKeepalivesStillValid(nai);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package com.android.server.connectivity;
|
|||||||
|
|
||||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
import static android.net.NattSocketKeepalive.NATT_PORT;
|
import static android.net.NattSocketKeepalive.NATT_PORT;
|
||||||
import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
|
|
||||||
import static android.net.SocketKeepalive.BINDER_DIED;
|
import static android.net.SocketKeepalive.BINDER_DIED;
|
||||||
import static android.net.SocketKeepalive.DATA_RECEIVED;
|
import static android.net.SocketKeepalive.DATA_RECEIVED;
|
||||||
import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
|
import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
|
||||||
@@ -88,7 +87,6 @@ public class KeepaliveTracker {
|
|||||||
/** Keeps track of keepalive requests. */
|
/** Keeps track of keepalive requests. */
|
||||||
private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
|
private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
|
||||||
new HashMap<> ();
|
new HashMap<> ();
|
||||||
private final Handler mConnectivityServiceHandler;
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final TcpKeepaliveController mTcpController;
|
private final TcpKeepaliveController mTcpController;
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -109,7 +107,6 @@ public class KeepaliveTracker {
|
|||||||
private final int mAllowedUnprivilegedSlotsForUid;
|
private final int mAllowedUnprivilegedSlotsForUid;
|
||||||
|
|
||||||
public KeepaliveTracker(Context context, Handler handler) {
|
public KeepaliveTracker(Context context, Handler handler) {
|
||||||
mConnectivityServiceHandler = handler;
|
|
||||||
mTcpController = new TcpKeepaliveController(handler);
|
mTcpController = new TcpKeepaliveController(handler);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
|
||||||
@@ -130,13 +127,13 @@ public class KeepaliveTracker {
|
|||||||
*/
|
*/
|
||||||
class KeepaliveInfo implements IBinder.DeathRecipient {
|
class KeepaliveInfo implements IBinder.DeathRecipient {
|
||||||
// Bookkeeping data.
|
// Bookkeeping data.
|
||||||
private final ISocketKeepaliveCallback mCallback;
|
public final ISocketKeepaliveCallback mCallback;
|
||||||
private final int mUid;
|
private final int mUid;
|
||||||
private final int mPid;
|
private final int mPid;
|
||||||
private final boolean mPrivileged;
|
private final boolean mPrivileged;
|
||||||
private final NetworkAgentInfo mNai;
|
public final NetworkAgentInfo mNai;
|
||||||
private final int mType;
|
private final int mType;
|
||||||
private final FileDescriptor mFd;
|
public final FileDescriptor mFd;
|
||||||
|
|
||||||
public static final int TYPE_NATT = 1;
|
public static final int TYPE_NATT = 1;
|
||||||
public static final int TYPE_TCP = 2;
|
public static final int TYPE_TCP = 2;
|
||||||
@@ -244,6 +241,10 @@ public class KeepaliveTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getSlot() {
|
||||||
|
return mSlot;
|
||||||
|
}
|
||||||
|
|
||||||
private int checkNetworkConnected() {
|
private int checkNetworkConnected() {
|
||||||
if (!mNai.networkInfo.isConnectedOrConnecting()) {
|
if (!mNai.networkInfo.isConnectedOrConnecting()) {
|
||||||
return ERROR_INVALID_NETWORK;
|
return ERROR_INVALID_NETWORK;
|
||||||
@@ -416,6 +417,13 @@ public class KeepaliveTracker {
|
|||||||
void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) {
|
void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) {
|
||||||
handleStopKeepalive(mNai, mSlot, socketKeepaliveReason);
|
handleStopKeepalive(mNai, mSlot, socketKeepaliveReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new KeepaliveInfo from existing KeepaliveInfo with a new fd.
|
||||||
|
*/
|
||||||
|
public KeepaliveInfo withFd(@NonNull FileDescriptor fd) throws InvalidSocketException {
|
||||||
|
return new KeepaliveInfo(mCallback, mNai, mPacket, mInterval, mType, fd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) {
|
void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) {
|
||||||
@@ -445,6 +453,9 @@ public class KeepaliveTracker {
|
|||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle start keepalives with the message.
|
||||||
|
*/
|
||||||
public void handleStartKeepalive(Message message) {
|
public void handleStartKeepalive(Message message) {
|
||||||
KeepaliveInfo ki = (KeepaliveInfo) message.obj;
|
KeepaliveInfo ki = (KeepaliveInfo) message.obj;
|
||||||
NetworkAgentInfo nai = ki.getNai();
|
NetworkAgentInfo nai = ki.getNai();
|
||||||
@@ -605,7 +616,8 @@ public class KeepaliveTracker {
|
|||||||
* Called when requesting that keepalives be started on a IPsec NAT-T socket. See
|
* Called when requesting that keepalives be started on a IPsec NAT-T socket. See
|
||||||
* {@link android.net.SocketKeepalive}.
|
* {@link android.net.SocketKeepalive}.
|
||||||
**/
|
**/
|
||||||
public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
|
@Nullable
|
||||||
|
public KeepaliveInfo makeNattKeepaliveInfo(@Nullable NetworkAgentInfo nai,
|
||||||
@Nullable FileDescriptor fd,
|
@Nullable FileDescriptor fd,
|
||||||
int intervalSeconds,
|
int intervalSeconds,
|
||||||
@NonNull ISocketKeepaliveCallback cb,
|
@NonNull ISocketKeepaliveCallback cb,
|
||||||
@@ -615,7 +627,7 @@ public class KeepaliveTracker {
|
|||||||
int dstPort) {
|
int dstPort) {
|
||||||
if (nai == null) {
|
if (nai == null) {
|
||||||
notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
|
notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
InetAddress srcAddress, dstAddress;
|
InetAddress srcAddress, dstAddress;
|
||||||
@@ -624,7 +636,7 @@ public class KeepaliveTracker {
|
|||||||
dstAddress = InetAddresses.parseNumericAddress(dstAddrString);
|
dstAddress = InetAddresses.parseNumericAddress(dstAddrString);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
notifyErrorCallback(cb, ERROR_INVALID_IP_ADDRESS);
|
notifyErrorCallback(cb, ERROR_INVALID_IP_ADDRESS);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeepalivePacketData packet;
|
KeepalivePacketData packet;
|
||||||
@@ -633,7 +645,7 @@ public class KeepaliveTracker {
|
|||||||
srcAddress, srcPort, dstAddress, NATT_PORT);
|
srcAddress, srcPort, dstAddress, NATT_PORT);
|
||||||
} catch (InvalidPacketException e) {
|
} catch (InvalidPacketException e) {
|
||||||
notifyErrorCallback(cb, e.getError());
|
notifyErrorCallback(cb, e.getError());
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
KeepaliveInfo ki = null;
|
KeepaliveInfo ki = null;
|
||||||
try {
|
try {
|
||||||
@@ -642,15 +654,14 @@ public class KeepaliveTracker {
|
|||||||
} catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
|
} catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
|
||||||
Log.e(TAG, "Fail to construct keepalive", e);
|
Log.e(TAG, "Fail to construct keepalive", e);
|
||||||
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
|
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Created keepalive: " + ki.toString());
|
Log.d(TAG, "Created keepalive: " + ki);
|
||||||
mConnectivityServiceHandler.obtainMessage(
|
return ki;
|
||||||
NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by ConnectivityService to start TCP keepalive on a file descriptor.
|
* Make a KeepaliveInfo for a TCP socket.
|
||||||
*
|
*
|
||||||
* In order to offload keepalive for application correctly, sequence number, ack number and
|
* In order to offload keepalive for application correctly, sequence number, ack number and
|
||||||
* other fields are needed to form the keepalive packet. Thus, this function synchronously
|
* other fields are needed to form the keepalive packet. Thus, this function synchronously
|
||||||
@@ -659,13 +670,14 @@ public class KeepaliveTracker {
|
|||||||
*
|
*
|
||||||
* See {@link android.net.SocketKeepalive}.
|
* See {@link android.net.SocketKeepalive}.
|
||||||
**/
|
**/
|
||||||
public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
|
@Nullable
|
||||||
|
public KeepaliveInfo makeTcpKeepaliveInfo(@Nullable NetworkAgentInfo nai,
|
||||||
@NonNull FileDescriptor fd,
|
@NonNull FileDescriptor fd,
|
||||||
int intervalSeconds,
|
int intervalSeconds,
|
||||||
@NonNull ISocketKeepaliveCallback cb) {
|
@NonNull ISocketKeepaliveCallback cb) {
|
||||||
if (nai == null) {
|
if (nai == null) {
|
||||||
notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
|
notifyErrorCallback(cb, ERROR_INVALID_NETWORK);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final TcpKeepalivePacketData packet;
|
final TcpKeepalivePacketData packet;
|
||||||
@@ -673,10 +685,10 @@ public class KeepaliveTracker {
|
|||||||
packet = TcpKeepaliveController.getTcpKeepalivePacket(fd);
|
packet = TcpKeepaliveController.getTcpKeepalivePacket(fd);
|
||||||
} catch (InvalidSocketException e) {
|
} catch (InvalidSocketException e) {
|
||||||
notifyErrorCallback(cb, e.error);
|
notifyErrorCallback(cb, e.error);
|
||||||
return;
|
return null;
|
||||||
} catch (InvalidPacketException e) {
|
} catch (InvalidPacketException e) {
|
||||||
notifyErrorCallback(cb, e.getError());
|
notifyErrorCallback(cb, e.getError());
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
KeepaliveInfo ki = null;
|
KeepaliveInfo ki = null;
|
||||||
try {
|
try {
|
||||||
@@ -685,20 +697,22 @@ public class KeepaliveTracker {
|
|||||||
} catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
|
} catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
|
||||||
Log.e(TAG, "Fail to construct keepalive e=" + e);
|
Log.e(TAG, "Fail to construct keepalive e=" + e);
|
||||||
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
|
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Created keepalive: " + ki.toString());
|
Log.d(TAG, "Created keepalive: " + ki.toString());
|
||||||
mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
|
return ki;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when requesting that keepalives be started on a IPsec NAT-T socket. This function is
|
* Make a KeepaliveInfo for an IPSec NAT-T socket.
|
||||||
* identical to {@link #startNattKeepalive}, but also takes a {@code resourceId}, which is the
|
*
|
||||||
* resource index bound to the {@link UdpEncapsulationSocket} when creating by
|
* This function is identical to {@link #makeNattKeepaliveInfo}, but also takes a
|
||||||
* {@link com.android.server.IpSecService} to verify whether the given
|
* {@code resourceId}, which is the resource index bound to the {@link UdpEncapsulationSocket}
|
||||||
* {@link UdpEncapsulationSocket} is legitimate.
|
* when creating by {@link com.android.server.IpSecService} to verify whether the given
|
||||||
**/
|
* {@link UdpEncapsulationSocket} is legitimate.
|
||||||
public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
|
**/
|
||||||
|
@Nullable
|
||||||
|
public KeepaliveInfo makeNattKeepaliveInfo(@Nullable NetworkAgentInfo nai,
|
||||||
@Nullable FileDescriptor fd,
|
@Nullable FileDescriptor fd,
|
||||||
int resourceId,
|
int resourceId,
|
||||||
int intervalSeconds,
|
int intervalSeconds,
|
||||||
@@ -709,6 +723,7 @@ public class KeepaliveTracker {
|
|||||||
// Ensure that the socket is created by IpSecService.
|
// Ensure that the socket is created by IpSecService.
|
||||||
if (!isNattKeepaliveSocketValid(fd, resourceId)) {
|
if (!isNattKeepaliveSocketValid(fd, resourceId)) {
|
||||||
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
|
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get src port to adopt old API.
|
// Get src port to adopt old API.
|
||||||
@@ -718,10 +733,11 @@ public class KeepaliveTracker {
|
|||||||
srcPort = ((InetSocketAddress) srcSockAddr).getPort();
|
srcPort = ((InetSocketAddress) srcSockAddr).getPort();
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
|
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward request to old API.
|
// Forward request to old API.
|
||||||
startNattKeepalive(nai, fd, intervalSeconds, cb, srcAddrString, srcPort,
|
return makeNattKeepaliveInfo(nai, fd, intervalSeconds, cb, srcAddrString, srcPort,
|
||||||
dstAddrString, dstPort);
|
dstAddrString, dstPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user