blob: 10daf17a7a9ab0b8d3d41b8a9af29dab4570d879 [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;
chiachangwangc51a7052023-03-13 02:25:44 +000024import 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 /**
Chalard Jeanbdb82822023-01-19 23:14:02 +090068 * Success when trying to suspend.
69 * @hide
70 */
71 public static final int SUCCESS_PAUSED = 1;
72
73 /**
lifr75764c82021-03-18 01:11:30 +080074 * No keepalive. This should only be internally as it indicates There is no keepalive.
75 * It should not propagate to applications.
76 * @hide
77 */
junyulai4c95b082018-12-27 17:25:29 +080078 public static final int NO_KEEPALIVE = -1;
79
lifr75764c82021-03-18 01:11:30 +080080 /**
81 * Data received.
82 * @hide
83 */
junyulai4c95b082018-12-27 17:25:29 +080084 public static final int DATA_RECEIVED = -2;
85
lifr75764c82021-03-18 01:11:30 +080086 /**
87 * The binder died.
88 * @hide
89 */
junyulai4c95b082018-12-27 17:25:29 +080090 public static final int BINDER_DIED = -10;
91
lifr75764c82021-03-18 01:11:30 +080092 /**
93 * The invalid network. It indicates the specified {@code Network} is not connected.
94 */
junyulai4c95b082018-12-27 17:25:29 +080095 public static final int ERROR_INVALID_NETWORK = -20;
lifr75764c82021-03-18 01:11:30 +080096
97 /**
98 * The invalid IP addresses. Indicates the specified IP addresses are invalid.
99 * For example, the specified source IP address is not configured on the
100 * specified {@code Network}.
101 */
junyulai4c95b082018-12-27 17:25:29 +0800102 public static final int ERROR_INVALID_IP_ADDRESS = -21;
lifr75764c82021-03-18 01:11:30 +0800103
104 /**
105 * The port is invalid.
106 */
junyulai4c95b082018-12-27 17:25:29 +0800107 public static final int ERROR_INVALID_PORT = -22;
lifr75764c82021-03-18 01:11:30 +0800108
109 /**
110 * The length is invalid (e.g. too long).
111 */
junyulai4c95b082018-12-27 17:25:29 +0800112 public static final int ERROR_INVALID_LENGTH = -23;
lifr75764c82021-03-18 01:11:30 +0800113
114 /**
115 * The interval is invalid (e.g. too short).
116 */
junyulai4c95b082018-12-27 17:25:29 +0800117 public static final int ERROR_INVALID_INTERVAL = -24;
lifr75764c82021-03-18 01:11:30 +0800118
119 /**
120 * The socket is invalid.
121 */
junyulai4c95b082018-12-27 17:25:29 +0800122 public static final int ERROR_INVALID_SOCKET = -25;
lifr75764c82021-03-18 01:11:30 +0800123
124 /**
125 * The socket is not idle.
126 */
junyulai4c95b082018-12-27 17:25:29 +0800127 public static final int ERROR_SOCKET_NOT_IDLE = -26;
lifr75764c82021-03-18 01:11:30 +0800128
junyulai75125f22019-09-03 18:51:04 +0800129 /**
130 * The stop reason is uninitialized. This should only be internally used as initial state
131 * of stop reason, instead of propagating to application.
132 * @hide
133 */
134 public static final int ERROR_STOP_REASON_UNINITIALIZED = -27;
junyulai4c95b082018-12-27 17:25:29 +0800135
lifr75764c82021-03-18 01:11:30 +0800136 /**
137 * The request is unsupported.
138 */
junyulai7e06ad42019-03-04 22:45:36 +0800139 public static final int ERROR_UNSUPPORTED = -30;
lifr75764c82021-03-18 01:11:30 +0800140
141 /**
142 * There was a hardware error.
143 */
junyulai4c95b082018-12-27 17:25:29 +0800144 public static final int ERROR_HARDWARE_ERROR = -31;
lifr75764c82021-03-18 01:11:30 +0800145
146 /**
147 * Resources are insufficient (e.g. all hardware slots are in use).
148 */
junyulai7e06ad42019-03-04 22:45:36 +0800149 public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
150
lifr75764c82021-03-18 01:11:30 +0800151 /**
152 * There was no such slot. This should only be internally as it indicates
153 * a programming error in the system server. It should not propagate to
154 * applications.
155 * @hide
156 */
157 @SystemApi
158 public static final int ERROR_NO_SUCH_SLOT = -33;
junyulai4c95b082018-12-27 17:25:29 +0800159
160 /** @hide */
161 @Retention(RetentionPolicy.SOURCE)
162 @IntDef(prefix = { "ERROR_" }, value = {
163 ERROR_INVALID_NETWORK,
164 ERROR_INVALID_IP_ADDRESS,
165 ERROR_INVALID_PORT,
166 ERROR_INVALID_LENGTH,
167 ERROR_INVALID_INTERVAL,
168 ERROR_INVALID_SOCKET,
lifr75764c82021-03-18 01:11:30 +0800169 ERROR_SOCKET_NOT_IDLE,
170 ERROR_NO_SUCH_SLOT
junyulai4c95b082018-12-27 17:25:29 +0800171 })
172 public @interface ErrorCode {}
173
Chalard Jeanf5d1bfd2020-03-24 23:16:35 +0900174 /** @hide */
175 @Retention(RetentionPolicy.SOURCE)
176 @IntDef(value = {
177 SUCCESS,
178 ERROR_INVALID_LENGTH,
179 ERROR_UNSUPPORTED,
Chalard Jean691a34d2020-03-27 15:00:38 +0900180 ERROR_INSUFFICIENT_RESOURCES,
Chalard Jeanf5d1bfd2020-03-24 23:16:35 +0900181 })
182 public @interface KeepaliveEvent {}
183
junyulai4c95b082018-12-27 17:25:29 +0800184 /**
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000185 * Whether the system automatically toggles keepalive when no TCP connection is open on the VPN.
186 *
187 * If this flag is present, the system will monitor the VPN(s) running on top of the specified
188 * network for open TCP connections. When no such connections are open, it will turn off the
189 * keepalives to conserve battery power. When there is at least one such connection it will
190 * turn on the keepalives to make sure functionality is preserved.
191 *
192 * This only works with {@link NattSocketKeepalive}.
193 * @hide
194 */
195 @SystemApi
196 public static final int FLAG_AUTOMATIC_ON_OFF = 1 << 0;
197
198 /** @hide */
199 @Retention(RetentionPolicy.SOURCE)
200 @IntDef(prefix = { "FLAG_"}, flag = true, value = {
201 FLAG_AUTOMATIC_ON_OFF
202 })
203 public @interface StartFlags {}
204
205 /**
junyulai4c95b082018-12-27 17:25:29 +0800206 * The minimum interval in seconds between keepalive packet transmissions.
207 *
208 * @hide
209 **/
210 public static final int MIN_INTERVAL_SEC = 10;
211
212 /**
213 * The maximum interval in seconds between keepalive packet transmissions.
214 *
215 * @hide
216 **/
217 public static final int MAX_INTERVAL_SEC = 3600;
218
junyulai011b1f12019-01-03 18:50:15 +0800219 /**
markchiene5591ce2018-12-27 22:49:51 +0800220 * An exception that embarks an error code.
221 * @hide
222 */
223 public static class ErrorCodeException extends Exception {
224 public final int error;
225 public ErrorCodeException(final int error, final Throwable e) {
226 super(e);
227 this.error = error;
228 }
229 public ErrorCodeException(final int error) {
230 this.error = error;
231 }
232 }
233
234 /**
235 * This socket is invalid.
236 * See the error code for details, and the optional cause.
237 * @hide
238 */
239 public static class InvalidSocketException extends ErrorCodeException {
240 public InvalidSocketException(final int error, final Throwable e) {
241 super(error, e);
242 }
243 public InvalidSocketException(final int error) {
244 super(error);
245 }
246 }
247
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900248 /** @hide */
249 @NonNull protected final IConnectivityManager mService;
250 /** @hide */
251 @NonNull protected final Network mNetwork;
252 /** @hide */
253 @NonNull protected final ParcelFileDescriptor mPfd;
254 /** @hide */
255 @NonNull protected final Executor mExecutor;
256 /** @hide */
257 @NonNull protected final ISocketKeepaliveCallback mCallback;
junyulai4c95b082018-12-27 17:25:29 +0800258
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900259 /** @hide */
260 public SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
junyulai7e06ad42019-03-04 22:45:36 +0800261 @NonNull ParcelFileDescriptor pfd,
junyulai4c95b082018-12-27 17:25:29 +0800262 @NonNull Executor executor, @NonNull Callback callback) {
263 mService = service;
264 mNetwork = network;
junyulai7e06ad42019-03-04 22:45:36 +0800265 mPfd = pfd;
junyulai4c95b082018-12-27 17:25:29 +0800266 mExecutor = executor;
junyulai070f9ff2019-01-16 20:23:34 +0800267 mCallback = new ISocketKeepaliveCallback.Stub() {
junyulai4c95b082018-12-27 17:25:29 +0800268 @Override
Chalard Jeanf0b261e2023-02-03 22:11:20 +0900269 public void onStarted() {
lucaslinbe801382020-12-30 11:54:55 +0800270 final long token = Binder.clearCallingIdentity();
271 try {
272 mExecutor.execute(() -> {
lucaslinbe801382020-12-30 11:54:55 +0800273 callback.onStarted();
274 });
275 } finally {
276 Binder.restoreCallingIdentity(token);
277 }
junyulai4c95b082018-12-27 17:25:29 +0800278 }
junyulai070f9ff2019-01-16 20:23:34 +0800279
280 @Override
Chalard Jeanbdb82822023-01-19 23:14:02 +0900281 public void onResumed() {
282 final long token = Binder.clearCallingIdentity();
283 try {
284 mExecutor.execute(() -> {
285 callback.onResumed();
286 });
287 } finally {
288 Binder.restoreCallingIdentity(token);
289 }
290 }
291
292 @Override
junyulai070f9ff2019-01-16 20:23:34 +0800293 public void onStopped() {
lucaslinbe801382020-12-30 11:54:55 +0800294 final long token = Binder.clearCallingIdentity();
295 try {
296 executor.execute(() -> {
lucaslinbe801382020-12-30 11:54:55 +0800297 callback.onStopped();
298 });
299 } finally {
300 Binder.restoreCallingIdentity(token);
301 }
junyulai070f9ff2019-01-16 20:23:34 +0800302 }
303
304 @Override
Chalard Jeanbdb82822023-01-19 23:14:02 +0900305 public void onPaused() {
306 final long token = Binder.clearCallingIdentity();
307 try {
308 executor.execute(() -> {
309 callback.onPaused();
310 });
311 } finally {
312 Binder.restoreCallingIdentity(token);
313 }
314 }
315
316 @Override
junyulai070f9ff2019-01-16 20:23:34 +0800317 public void onError(int error) {
lucaslinbe801382020-12-30 11:54:55 +0800318 final long token = Binder.clearCallingIdentity();
319 try {
320 executor.execute(() -> {
lucaslinbe801382020-12-30 11:54:55 +0800321 callback.onError(error);
322 });
323 } finally {
324 Binder.restoreCallingIdentity(token);
325 }
junyulai070f9ff2019-01-16 20:23:34 +0800326 }
327
328 @Override
329 public void onDataReceived() {
lucaslinbe801382020-12-30 11:54:55 +0800330 final long token = Binder.clearCallingIdentity();
331 try {
332 executor.execute(() -> {
lucaslinbe801382020-12-30 11:54:55 +0800333 callback.onDataReceived();
334 });
335 } finally {
336 Binder.restoreCallingIdentity(token);
337 }
junyulai070f9ff2019-01-16 20:23:34 +0800338 }
339 };
junyulai4c95b082018-12-27 17:25:29 +0800340 }
341
342 /**
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000343 * Request that keepalive be started with the given {@code intervalSec}.
344 *
345 * See {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an
346 * exception when invoking start or stop of the {@link SocketKeepalive}, a
347 * {@link RuntimeException} caused by a {@link RemoteException} will be thrown into the
348 * {@link Executor}. This is typically not important to catch because the remote party is
349 * the system, so if it is not in shape to communicate through binder the system is going
350 * down anyway. If the caller still cares, it can use a custom {@link Executor} to catch the
351 * {@link RuntimeException}.
junyulai4c95b082018-12-27 17:25:29 +0800352 *
353 * @param intervalSec The target interval in seconds between keepalive packet transmissions.
354 * The interval should be between 10 seconds and 3600 seconds, otherwise
355 * {@link #ERROR_INVALID_INTERVAL} will be returned.
356 */
357 public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
358 int intervalSec) {
chiachangwang676c84e2023-02-14 09:22:05 +0000359 startImpl(intervalSec, 0 /* flags */, null /* underpinnedNetwork */);
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000360 }
361
362 /**
363 * Request that keepalive be started with the given {@code intervalSec}.
364 *
365 * See {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an
366 * exception when invoking start or stop of the {@link SocketKeepalive}, a
367 * {@link RuntimeException} caused by a {@link RemoteException} will be thrown into the
368 * {@link Executor}. This is typically not important to catch because the remote party is
369 * the system, so if it is not in shape to communicate through binder the system is going
370 * down anyway. If the caller still cares, it can use a custom {@link Executor} to catch the
371 * {@link RuntimeException}.
372 *
373 * @param intervalSec The target interval in seconds between keepalive packet transmissions.
374 * The interval should be between 10 seconds and 3600 seconds. Otherwise,
375 * the supplied {@link Callback} will see a call to
376 * {@link Callback#onError(int)} with {@link #ERROR_INVALID_INTERVAL}.
377 * @param flags Flags to enable/disable available options on this keepalive.
chiachangwangc51a7052023-03-13 02:25:44 +0000378 * @param underpinnedNetwork an optional network running over mNetwork that this
379 * keepalive is intended to keep up, e.g. an IPSec
380 * tunnel running over mNetwork.
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000381 * @hide
382 */
383 @SystemApi(client = PRIVILEGED_APPS)
384 public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
chiachangwangc51a7052023-03-13 02:25:44 +0000385 int intervalSec, @StartFlags int flags, @Nullable Network underpinnedNetwork) {
chiachangwang676c84e2023-02-14 09:22:05 +0000386 startImpl(intervalSec, flags, underpinnedNetwork);
junyulai4c95b082018-12-27 17:25:29 +0800387 }
388
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900389 /** @hide */
chiachangwang676c84e2023-02-14 09:22:05 +0000390 protected abstract void startImpl(int intervalSec, @StartFlags int flags,
391 Network underpinnedNetwork);
junyulai4c95b082018-12-27 17:25:29 +0800392
junyulai4c95b082018-12-27 17:25:29 +0800393 /**
394 * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
395 * before using the object. See {@link SocketKeepalive}.
396 */
397 public final void stop() {
398 stopImpl();
399 }
400
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900401 /** @hide */
402 protected abstract void stopImpl();
junyulai4c95b082018-12-27 17:25:29 +0800403
404 /**
405 * Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be
406 * usable again if {@code close()} is called.
407 */
408 @Override
409 public final void close() {
410 stop();
junyulai7e06ad42019-03-04 22:45:36 +0800411 try {
412 mPfd.close();
413 } catch (IOException e) {
414 // Nothing much can be done.
415 }
junyulai4c95b082018-12-27 17:25:29 +0800416 }
417
418 /**
419 * The callback which app can use to learn the status changes of {@link SocketKeepalive}. See
420 * {@link SocketKeepalive}.
421 */
422 public static class Callback {
423 /** The requested keepalive was successfully started. */
424 public void onStarted() {}
Chalard Jeanbdb82822023-01-19 23:14:02 +0900425 /**
426 * The keepalive was resumed by the system after being suspended.
427 * @hide
428 **/
429 public void onResumed() {}
junyulai4c95b082018-12-27 17:25:29 +0800430 /** The keepalive was successfully stopped. */
431 public void onStopped() {}
Chalard Jeanbdb82822023-01-19 23:14:02 +0900432 /**
433 * The keepalive was paused by the system because it's not necessary right now.
434 * @hide
435 **/
436 public void onPaused() {}
junyulai4c95b082018-12-27 17:25:29 +0800437 /** An error occurred. */
438 public void onError(@ErrorCode int error) {}
junyulai070f9ff2019-01-16 20:23:34 +0800439 /** The keepalive on a TCP socket was stopped because the socket received data. This is
440 * never called for UDP sockets. */
junyulai4c95b082018-12-27 17:25:29 +0800441 public void onDataReceived() {}
442 }
443}