android_packages_modules_Co.../framework/src/android/net/SocketKeepalive.java
chiachangwang 676c84ef98 Add underpinned Network parameter in SocketKeepalive.start()
Take a Network parameter to have an one-to-one mapping between
keepalive and its underpinned network on the automatic keepalive.

Existing design could not really tell which network should the
automatic keepalive check for the TCP socket status if there are
multiple automatic keepalives enabled, e.g. Bothe IWLAN and VPN
on WiFi enable the automatic keepalive. The keepalive for IWLAN
should check if there are any TCP sockets on the IWLAN network
instead of VPN network.

Bug: 259000745
Test: atest FrameworksNetTests
Test: Cts in the follow up commit
Change-Id: I7353f4ef43e8fdad02c4d4a0bb5f6efa7d94c1b4
2023-02-14 10:29:19 +00:00

440 lines
15 KiB
Java

/*
* 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 static android.annotation.SystemApi.Client.PRIVILEGED_APPS;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
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;
/**
* Allows applications to request that the system periodically send specific packets on their
* behalf, using hardware offload to save battery power.
*
* To request that the system send keepalives, call one of the methods that return a
* {@link SocketKeepalive} object, such as {@link ConnectivityManager#createSocketKeepalive},
* passing in a non-null callback. If the {@link SocketKeepalive} is successfully
* started, the callback's {@code onStarted} method will be called. If an error occurs,
* {@code onError} will be called, specifying one of the {@code ERROR_*} constants in this
* class.
*
* To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call
* {@link SocketKeepalive.Callback#onStopped} if the operation was successful or
* {@link SocketKeepalive.Callback#onError} if an error occurred.
*
* For cellular, the device MUST support at least 1 keepalive slot.
*
* For WiFi, the device SHOULD support keepalive offload. If it does not, it MUST reply with
* {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload
* request. If it does, it MUST support at least 3 concurrent keepalive slots.
*/
public abstract class SocketKeepalive implements AutoCloseable {
/** @hide */
protected static final String TAG = "SocketKeepalive";
/**
* Success. It indicates there is no error.
* @hide
*/
@SystemApi
public static final int SUCCESS = 0;
/**
* Success when trying to suspend.
* @hide
*/
public static final int SUCCESS_PAUSED = 1;
/**
* No keepalive. This should only be internally as it indicates There is no keepalive.
* It should not propagate to applications.
* @hide
*/
public static final int NO_KEEPALIVE = -1;
/**
* Data received.
* @hide
*/
public static final int DATA_RECEIVED = -2;
/**
* The binder died.
* @hide
*/
public static final int BINDER_DIED = -10;
/**
* The invalid network. It indicates the specified {@code Network} is not connected.
*/
public static final int ERROR_INVALID_NETWORK = -20;
/**
* The invalid IP addresses. Indicates the specified IP addresses are invalid.
* For example, the specified source IP address is not configured on the
* specified {@code Network}.
*/
public static final int ERROR_INVALID_IP_ADDRESS = -21;
/**
* The port is invalid.
*/
public static final int ERROR_INVALID_PORT = -22;
/**
* The length is invalid (e.g. too long).
*/
public static final int ERROR_INVALID_LENGTH = -23;
/**
* The interval is invalid (e.g. too short).
*/
public static final int ERROR_INVALID_INTERVAL = -24;
/**
* The socket is invalid.
*/
public static final int ERROR_INVALID_SOCKET = -25;
/**
* The socket is not idle.
*/
public static final int ERROR_SOCKET_NOT_IDLE = -26;
/**
* The stop reason is uninitialized. This should only be internally used as initial state
* of stop reason, instead of propagating to application.
* @hide
*/
public static final int ERROR_STOP_REASON_UNINITIALIZED = -27;
/**
* The request is unsupported.
*/
public static final int ERROR_UNSUPPORTED = -30;
/**
* There was a hardware error.
*/
public static final int ERROR_HARDWARE_ERROR = -31;
/**
* Resources are insufficient (e.g. all hardware slots are in use).
*/
public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
/**
* There was no such slot. This should only be internally as it indicates
* a programming error in the system server. It should not propagate to
* applications.
* @hide
*/
@SystemApi
public static final int ERROR_NO_SUCH_SLOT = -33;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "ERROR_" }, value = {
ERROR_INVALID_NETWORK,
ERROR_INVALID_IP_ADDRESS,
ERROR_INVALID_PORT,
ERROR_INVALID_LENGTH,
ERROR_INVALID_INTERVAL,
ERROR_INVALID_SOCKET,
ERROR_SOCKET_NOT_IDLE,
ERROR_NO_SUCH_SLOT
})
public @interface ErrorCode {}
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
SUCCESS,
ERROR_INVALID_LENGTH,
ERROR_UNSUPPORTED,
ERROR_INSUFFICIENT_RESOURCES,
})
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.
*
* @hide
**/
public static final int MIN_INTERVAL_SEC = 10;
/**
* The maximum interval in seconds between keepalive packet transmissions.
*
* @hide
**/
public static final int MAX_INTERVAL_SEC = 3600;
/**
* An exception that embarks an error code.
* @hide
*/
public static class ErrorCodeException extends Exception {
public final int error;
public ErrorCodeException(final int error, final Throwable e) {
super(e);
this.error = error;
}
public ErrorCodeException(final int error) {
this.error = error;
}
}
/**
* This socket is invalid.
* See the error code for details, and the optional cause.
* @hide
*/
public static class InvalidSocketException extends ErrorCodeException {
public InvalidSocketException(final int error, final Throwable e) {
super(error, e);
}
public InvalidSocketException(final int error) {
super(error);
}
}
/** @hide */
@NonNull protected final IConnectivityManager mService;
/** @hide */
@NonNull protected final Network mNetwork;
/** @hide */
@NonNull protected final ParcelFileDescriptor mPfd;
/** @hide */
@NonNull protected final Executor mExecutor;
/** @hide */
@NonNull protected final ISocketKeepaliveCallback mCallback;
/** @hide */
public 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
public void onStarted() {
final long token = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> {
callback.onStarted();
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onResumed() {
final long token = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> {
callback.onResumed();
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onStopped() {
final long token = Binder.clearCallingIdentity();
try {
executor.execute(() -> {
callback.onStopped();
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onPaused() {
final long token = Binder.clearCallingIdentity();
try {
executor.execute(() -> {
callback.onPaused();
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onError(int error) {
final long token = Binder.clearCallingIdentity();
try {
executor.execute(() -> {
callback.onError(error);
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onDataReceived() {
final long token = Binder.clearCallingIdentity();
try {
executor.execute(() -> {
callback.onDataReceived();
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
};
}
/**
* 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
* {@link #ERROR_INVALID_INTERVAL} will be returned.
*/
public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
int intervalSec) {
startImpl(intervalSec, 0 /* flags */, null /* underpinnedNetwork */);
}
/**
* 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.
* @param underpinnedNetwork The underpinned network of 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, @NonNull Network underpinnedNetwork) {
startImpl(intervalSec, flags, underpinnedNetwork);
}
/** @hide */
protected abstract void startImpl(int intervalSec, @StartFlags int flags,
Network underpinnedNetwork);
/**
* Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
* before using the object. See {@link SocketKeepalive}.
*/
public final void stop() {
stopImpl();
}
/** @hide */
protected abstract void stopImpl();
/**
* Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be
* usable again if {@code close()} is called.
*/
@Override
public final void close() {
stop();
try {
mPfd.close();
} catch (IOException e) {
// Nothing much can be done.
}
}
/**
* The callback which app can use to learn the status changes of {@link SocketKeepalive}. See
* {@link SocketKeepalive}.
*/
public static class Callback {
/** The requested keepalive was successfully started. */
public void onStarted() {}
/**
* The keepalive was resumed by the system after being suspended.
* @hide
**/
public void onResumed() {}
/** The keepalive was successfully stopped. */
public void onStopped() {}
/**
* The keepalive was paused by the system because it's not necessary right now.
* @hide
**/
public void onPaused() {}
/** An error occurred. */
public void onError(@ErrorCode int error) {}
/** The keepalive on a TCP socket was stopped because the socket received data. This is
* never called for UDP sockets. */
public void onDataReceived() {}
}
}