blob: 90e5e9b34f34aa79c589e48ca551ae8d62e7c4a6 [file] [log] [blame]
junyulai4c95b082018-12-27 17:25:29 +08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
chiachangwang9ef4ffe2023-01-18 01:19:27 +000019import static android.annotation.SystemApi.Client.PRIVILEGED_APPS;
20
junyulai4c95b082018-12-27 17:25:29 +080021import android.annotation.IntDef;
22import android.annotation.IntRange;
23import android.annotation.NonNull;
junyulai0835a1e2019-01-08 20:04:33 +080024import android.annotation.Nullable;
paulhu2f7c3452020-01-13 16:18:54 +080025import android.annotation.SystemApi;
junyulai070f9ff2019-01-16 20:23:34 +080026import android.os.Binder;
junyulai7e06ad42019-03-04 22:45:36 +080027import android.os.ParcelFileDescriptor;
junyulai070f9ff2019-01-16 20:23:34 +080028import android.os.RemoteException;
junyulai4c95b082018-12-27 17:25:29 +080029
junyulai7e06ad42019-03-04 22:45:36 +080030import java.io.IOException;
junyulai4c95b082018-12-27 17:25:29 +080031import java.lang.annotation.Retention;
32import java.lang.annotation.RetentionPolicy;
33import java.util.concurrent.Executor;
34
35/**
36 * Allows applications to request that the system periodically send specific packets on their
37 * behalf, using hardware offload to save battery power.
38 *
39 * To request that the system send keepalives, call one of the methods that return a
40 * {@link SocketKeepalive} object, such as {@link ConnectivityManager#createSocketKeepalive},
41 * passing in a non-null callback. If the {@link SocketKeepalive} is successfully
42 * started, the callback's {@code onStarted} method will be called. If an error occurs,
43 * {@code onError} will be called, specifying one of the {@code ERROR_*} constants in this
44 * class.
45 *
46 * To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call
47 * {@link SocketKeepalive.Callback#onStopped} if the operation was successful or
48 * {@link SocketKeepalive.Callback#onError} if an error occurred.
junyulai4dca18a2019-04-17 15:22:46 +080049 *
Aaron Huangede0d502019-06-05 17:09:29 +080050 * For cellular, the device MUST support at least 1 keepalive slot.
51 *
52 * For WiFi, the device SHOULD support keepalive offload. If it does not, it MUST reply with
junyulai4dca18a2019-04-17 15:22:46 +080053 * {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload
Aaron Huangede0d502019-06-05 17:09:29 +080054 * request. If it does, it MUST support at least 3 concurrent keepalive slots.
junyulai4c95b082018-12-27 17:25:29 +080055 */
56public abstract class SocketKeepalive implements AutoCloseable {
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +090057 /** @hide */
58 protected static final String TAG = "SocketKeepalive";
junyulai4c95b082018-12-27 17:25:29 +080059
paulhu2f7c3452020-01-13 16:18:54 +080060 /**
lifr75764c82021-03-18 01:11:30 +080061 * Success. It indicates there is no error.
paulhu2f7c3452020-01-13 16:18:54 +080062 * @hide
63 */
64 @SystemApi
junyulai4c95b082018-12-27 17:25:29 +080065 public static final int SUCCESS = 0;
66
lifr75764c82021-03-18 01:11:30 +080067 /**
68 * No keepalive. This should only be internally as it indicates There is no keepalive.
69 * It should not propagate to applications.
70 * @hide
71 */
junyulai4c95b082018-12-27 17:25:29 +080072 public static final int NO_KEEPALIVE = -1;
73
lifr75764c82021-03-18 01:11:30 +080074 /**
75 * Data received.
76 * @hide
77 */
junyulai4c95b082018-12-27 17:25:29 +080078 public static final int DATA_RECEIVED = -2;
79
lifr75764c82021-03-18 01:11:30 +080080 /**
81 * The binder died.
82 * @hide
83 */
junyulai4c95b082018-12-27 17:25:29 +080084 public static final int BINDER_DIED = -10;
85
lifr75764c82021-03-18 01:11:30 +080086 /**
87 * The invalid network. It indicates the specified {@code Network} is not connected.
88 */
junyulai4c95b082018-12-27 17:25:29 +080089 public static final int ERROR_INVALID_NETWORK = -20;
lifr75764c82021-03-18 01:11:30 +080090
91 /**
92 * The invalid IP addresses. Indicates the specified IP addresses are invalid.
93 * For example, the specified source IP address is not configured on the
94 * specified {@code Network}.
95 */
junyulai4c95b082018-12-27 17:25:29 +080096 public static final int ERROR_INVALID_IP_ADDRESS = -21;
lifr75764c82021-03-18 01:11:30 +080097
98 /**
99 * The port is invalid.
100 */
junyulai4c95b082018-12-27 17:25:29 +0800101 public static final int ERROR_INVALID_PORT = -22;
lifr75764c82021-03-18 01:11:30 +0800102
103 /**
104 * The length is invalid (e.g. too long).
105 */
junyulai4c95b082018-12-27 17:25:29 +0800106 public static final int ERROR_INVALID_LENGTH = -23;
lifr75764c82021-03-18 01:11:30 +0800107
108 /**
109 * The interval is invalid (e.g. too short).
110 */
junyulai4c95b082018-12-27 17:25:29 +0800111 public static final int ERROR_INVALID_INTERVAL = -24;
lifr75764c82021-03-18 01:11:30 +0800112
113 /**
114 * The socket is invalid.
115 */
junyulai4c95b082018-12-27 17:25:29 +0800116 public static final int ERROR_INVALID_SOCKET = -25;
lifr75764c82021-03-18 01:11:30 +0800117
118 /**
119 * The socket is not idle.
120 */
junyulai4c95b082018-12-27 17:25:29 +0800121 public static final int ERROR_SOCKET_NOT_IDLE = -26;
lifr75764c82021-03-18 01:11:30 +0800122
junyulai75125f22019-09-03 18:51:04 +0800123 /**
124 * The stop reason is uninitialized. This should only be internally used as initial state
125 * of stop reason, instead of propagating to application.
126 * @hide
127 */
128 public static final int ERROR_STOP_REASON_UNINITIALIZED = -27;
junyulai4c95b082018-12-27 17:25:29 +0800129
lifr75764c82021-03-18 01:11:30 +0800130 /**
131 * The request is unsupported.
132 */
junyulai7e06ad42019-03-04 22:45:36 +0800133 public static final int ERROR_UNSUPPORTED = -30;
lifr75764c82021-03-18 01:11:30 +0800134
135 /**
136 * There was a hardware error.
137 */
junyulai4c95b082018-12-27 17:25:29 +0800138 public static final int ERROR_HARDWARE_ERROR = -31;
lifr75764c82021-03-18 01:11:30 +0800139
140 /**
141 * Resources are insufficient (e.g. all hardware slots are in use).
142 */
junyulai7e06ad42019-03-04 22:45:36 +0800143 public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
144
lifr75764c82021-03-18 01:11:30 +0800145 /**
146 * There was no such slot. This should only be internally as it indicates
147 * a programming error in the system server. It should not propagate to
148 * applications.
149 * @hide
150 */
151 @SystemApi
152 public static final int ERROR_NO_SUCH_SLOT = -33;
junyulai4c95b082018-12-27 17:25:29 +0800153
154 /** @hide */
155 @Retention(RetentionPolicy.SOURCE)
156 @IntDef(prefix = { "ERROR_" }, value = {
157 ERROR_INVALID_NETWORK,
158 ERROR_INVALID_IP_ADDRESS,
159 ERROR_INVALID_PORT,
160 ERROR_INVALID_LENGTH,
161 ERROR_INVALID_INTERVAL,
162 ERROR_INVALID_SOCKET,
lifr75764c82021-03-18 01:11:30 +0800163 ERROR_SOCKET_NOT_IDLE,
164 ERROR_NO_SUCH_SLOT
junyulai4c95b082018-12-27 17:25:29 +0800165 })
166 public @interface ErrorCode {}
167
Chalard Jeanf5d1bfd2020-03-24 23:16:35 +0900168 /** @hide */
169 @Retention(RetentionPolicy.SOURCE)
170 @IntDef(value = {
171 SUCCESS,
172 ERROR_INVALID_LENGTH,
173 ERROR_UNSUPPORTED,
Chalard Jean691a34d2020-03-27 15:00:38 +0900174 ERROR_INSUFFICIENT_RESOURCES,
Chalard Jeanf5d1bfd2020-03-24 23:16:35 +0900175 })
176 public @interface KeepaliveEvent {}
177
junyulai4c95b082018-12-27 17:25:29 +0800178 /**
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000179 * Whether the system automatically toggles keepalive when no TCP connection is open on the VPN.
180 *
181 * If this flag is present, the system will monitor the VPN(s) running on top of the specified
182 * network for open TCP connections. When no such connections are open, it will turn off the
183 * keepalives to conserve battery power. When there is at least one such connection it will
184 * turn on the keepalives to make sure functionality is preserved.
185 *
186 * This only works with {@link NattSocketKeepalive}.
187 * @hide
188 */
189 @SystemApi
190 public static final int FLAG_AUTOMATIC_ON_OFF = 1 << 0;
191
192 /** @hide */
193 @Retention(RetentionPolicy.SOURCE)
194 @IntDef(prefix = { "FLAG_"}, flag = true, value = {
195 FLAG_AUTOMATIC_ON_OFF
196 })
197 public @interface StartFlags {}
198
199 /**
junyulai4c95b082018-12-27 17:25:29 +0800200 * The minimum interval in seconds between keepalive packet transmissions.
201 *
202 * @hide
203 **/
204 public static final int MIN_INTERVAL_SEC = 10;
205
206 /**
207 * The maximum interval in seconds between keepalive packet transmissions.
208 *
209 * @hide
210 **/
211 public static final int MAX_INTERVAL_SEC = 3600;
212
junyulai011b1f12019-01-03 18:50:15 +0800213 /**
markchiene5591ce2018-12-27 22:49:51 +0800214 * An exception that embarks an error code.
215 * @hide
216 */
217 public static class ErrorCodeException extends Exception {
218 public final int error;
219 public ErrorCodeException(final int error, final Throwable e) {
220 super(e);
221 this.error = error;
222 }
223 public ErrorCodeException(final int error) {
224 this.error = error;
225 }
226 }
227
228 /**
229 * This socket is invalid.
230 * See the error code for details, and the optional cause.
231 * @hide
232 */
233 public static class InvalidSocketException extends ErrorCodeException {
234 public InvalidSocketException(final int error, final Throwable e) {
235 super(error, e);
236 }
237 public InvalidSocketException(final int error) {
238 super(error);
239 }
240 }
241
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900242 /** @hide */
243 @NonNull protected final IConnectivityManager mService;
244 /** @hide */
245 @NonNull protected final Network mNetwork;
246 /** @hide */
247 @NonNull protected final ParcelFileDescriptor mPfd;
248 /** @hide */
249 @NonNull protected final Executor mExecutor;
250 /** @hide */
251 @NonNull protected final ISocketKeepaliveCallback mCallback;
junyulai070f9ff2019-01-16 20:23:34 +0800252 // TODO: remove slot since mCallback could be used to identify which keepalive to stop.
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900253 /** @hide */
254 @Nullable protected Integer mSlot;
junyulai4c95b082018-12-27 17:25:29 +0800255
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900256 /** @hide */
257 public SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
junyulai7e06ad42019-03-04 22:45:36 +0800258 @NonNull ParcelFileDescriptor pfd,
junyulai4c95b082018-12-27 17:25:29 +0800259 @NonNull Executor executor, @NonNull Callback callback) {
260 mService = service;
261 mNetwork = network;
junyulai7e06ad42019-03-04 22:45:36 +0800262 mPfd = pfd;
junyulai4c95b082018-12-27 17:25:29 +0800263 mExecutor = executor;
junyulai070f9ff2019-01-16 20:23:34 +0800264 mCallback = new ISocketKeepaliveCallback.Stub() {
junyulai4c95b082018-12-27 17:25:29 +0800265 @Override
junyulai070f9ff2019-01-16 20:23:34 +0800266 public void onStarted(int slot) {
lucaslinbe801382020-12-30 11:54:55 +0800267 final long token = Binder.clearCallingIdentity();
268 try {
269 mExecutor.execute(() -> {
270 mSlot = slot;
271 callback.onStarted();
272 });
273 } finally {
274 Binder.restoreCallingIdentity(token);
275 }
junyulai4c95b082018-12-27 17:25:29 +0800276 }
junyulai070f9ff2019-01-16 20:23:34 +0800277
278 @Override
279 public void onStopped() {
lucaslinbe801382020-12-30 11:54:55 +0800280 final long token = Binder.clearCallingIdentity();
281 try {
282 executor.execute(() -> {
283 mSlot = null;
284 callback.onStopped();
285 });
286 } finally {
287 Binder.restoreCallingIdentity(token);
288 }
junyulai070f9ff2019-01-16 20:23:34 +0800289 }
290
291 @Override
292 public void onError(int error) {
lucaslinbe801382020-12-30 11:54:55 +0800293 final long token = Binder.clearCallingIdentity();
294 try {
295 executor.execute(() -> {
296 mSlot = null;
297 callback.onError(error);
298 });
299 } finally {
300 Binder.restoreCallingIdentity(token);
301 }
junyulai070f9ff2019-01-16 20:23:34 +0800302 }
303
304 @Override
305 public void onDataReceived() {
lucaslinbe801382020-12-30 11:54:55 +0800306 final long token = Binder.clearCallingIdentity();
307 try {
308 executor.execute(() -> {
309 mSlot = null;
310 callback.onDataReceived();
311 });
312 } finally {
313 Binder.restoreCallingIdentity(token);
314 }
junyulai070f9ff2019-01-16 20:23:34 +0800315 }
316 };
junyulai4c95b082018-12-27 17:25:29 +0800317 }
318
319 /**
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000320 * Request that keepalive be started with the given {@code intervalSec}.
321 *
322 * See {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an
323 * exception when invoking start or stop of the {@link SocketKeepalive}, a
324 * {@link RuntimeException} caused by a {@link RemoteException} will be thrown into the
325 * {@link Executor}. This is typically not important to catch because the remote party is
326 * the system, so if it is not in shape to communicate through binder the system is going
327 * down anyway. If the caller still cares, it can use a custom {@link Executor} to catch the
328 * {@link RuntimeException}.
junyulai4c95b082018-12-27 17:25:29 +0800329 *
330 * @param intervalSec The target interval in seconds between keepalive packet transmissions.
331 * The interval should be between 10 seconds and 3600 seconds, otherwise
332 * {@link #ERROR_INVALID_INTERVAL} will be returned.
333 */
334 public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
335 int intervalSec) {
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000336 startImpl(intervalSec, 0 /* flags */);
337 }
338
339 /**
340 * Request that keepalive be started with the given {@code intervalSec}.
341 *
342 * See {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an
343 * exception when invoking start or stop of the {@link SocketKeepalive}, a
344 * {@link RuntimeException} caused by a {@link RemoteException} will be thrown into the
345 * {@link Executor}. This is typically not important to catch because the remote party is
346 * the system, so if it is not in shape to communicate through binder the system is going
347 * down anyway. If the caller still cares, it can use a custom {@link Executor} to catch the
348 * {@link RuntimeException}.
349 *
350 * @param intervalSec The target interval in seconds between keepalive packet transmissions.
351 * The interval should be between 10 seconds and 3600 seconds. Otherwise,
352 * the supplied {@link Callback} will see a call to
353 * {@link Callback#onError(int)} with {@link #ERROR_INVALID_INTERVAL}.
354 * @param flags Flags to enable/disable available options on this keepalive.
355 * @hide
356 */
357 @SystemApi(client = PRIVILEGED_APPS)
358 public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
359 int intervalSec, @StartFlags int flags) {
360 startImpl(intervalSec, flags);
junyulai4c95b082018-12-27 17:25:29 +0800361 }
362
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900363 /** @hide */
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000364 protected abstract void startImpl(int intervalSec, @StartFlags int flags);
junyulai4c95b082018-12-27 17:25:29 +0800365
junyulai4c95b082018-12-27 17:25:29 +0800366 /**
367 * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
368 * before using the object. See {@link SocketKeepalive}.
369 */
370 public final void stop() {
371 stopImpl();
372 }
373
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900374 /** @hide */
375 protected abstract void stopImpl();
junyulai4c95b082018-12-27 17:25:29 +0800376
377 /**
378 * Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be
379 * usable again if {@code close()} is called.
380 */
381 @Override
382 public final void close() {
383 stop();
junyulai7e06ad42019-03-04 22:45:36 +0800384 try {
385 mPfd.close();
386 } catch (IOException e) {
387 // Nothing much can be done.
388 }
junyulai4c95b082018-12-27 17:25:29 +0800389 }
390
391 /**
392 * The callback which app can use to learn the status changes of {@link SocketKeepalive}. See
393 * {@link SocketKeepalive}.
394 */
395 public static class Callback {
396 /** The requested keepalive was successfully started. */
397 public void onStarted() {}
398 /** The keepalive was successfully stopped. */
399 public void onStopped() {}
400 /** An error occurred. */
401 public void onError(@ErrorCode int error) {}
junyulai070f9ff2019-01-16 20:23:34 +0800402 /** The keepalive on a TCP socket was stopped because the socket received data. This is
403 * never called for UDP sockets. */
junyulai4c95b082018-12-27 17:25:29 +0800404 public void onDataReceived() {}
405 }
406}