442 lines
15 KiB
Java
442 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.Nullable;
|
|
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, or no keepalive running on this slot.
|
|
* @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 an optional network running over mNetwork that this
|
|
* keepalive is intended to keep up, e.g. an IPSec
|
|
* tunnel running over mNetwork.
|
|
* @hide
|
|
*/
|
|
@SystemApi(client = PRIVILEGED_APPS)
|
|
public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
|
|
int intervalSec, @StartFlags int flags, @Nullable 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() {}
|
|
}
|
|
}
|