blob: f915e725b1fccc78b9ac236773dab55612904823 [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 /**
chiachangwang75673ba2023-05-17 05:37:10 +0000152 * There was no such slot, or no keepalive running on this slot.
lifr75764c82021-03-18 01:11:30 +0800153 * @hide
154 */
155 @SystemApi
156 public static final int ERROR_NO_SUCH_SLOT = -33;
junyulai4c95b082018-12-27 17:25:29 +0800157
158 /** @hide */
159 @Retention(RetentionPolicy.SOURCE)
160 @IntDef(prefix = { "ERROR_" }, value = {
161 ERROR_INVALID_NETWORK,
162 ERROR_INVALID_IP_ADDRESS,
163 ERROR_INVALID_PORT,
164 ERROR_INVALID_LENGTH,
165 ERROR_INVALID_INTERVAL,
166 ERROR_INVALID_SOCKET,
lifr75764c82021-03-18 01:11:30 +0800167 ERROR_SOCKET_NOT_IDLE,
168 ERROR_NO_SUCH_SLOT
junyulai4c95b082018-12-27 17:25:29 +0800169 })
170 public @interface ErrorCode {}
171
Chalard Jeanf5d1bfd2020-03-24 23:16:35 +0900172 /** @hide */
173 @Retention(RetentionPolicy.SOURCE)
174 @IntDef(value = {
175 SUCCESS,
176 ERROR_INVALID_LENGTH,
177 ERROR_UNSUPPORTED,
Chalard Jean691a34d2020-03-27 15:00:38 +0900178 ERROR_INSUFFICIENT_RESOURCES,
Chalard Jeanf5d1bfd2020-03-24 23:16:35 +0900179 })
180 public @interface KeepaliveEvent {}
181
junyulai4c95b082018-12-27 17:25:29 +0800182 /**
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000183 * Whether the system automatically toggles keepalive when no TCP connection is open on the VPN.
184 *
185 * If this flag is present, the system will monitor the VPN(s) running on top of the specified
186 * network for open TCP connections. When no such connections are open, it will turn off the
187 * keepalives to conserve battery power. When there is at least one such connection it will
188 * turn on the keepalives to make sure functionality is preserved.
189 *
190 * This only works with {@link NattSocketKeepalive}.
191 * @hide
192 */
193 @SystemApi
194 public static final int FLAG_AUTOMATIC_ON_OFF = 1 << 0;
195
196 /** @hide */
197 @Retention(RetentionPolicy.SOURCE)
198 @IntDef(prefix = { "FLAG_"}, flag = true, value = {
199 FLAG_AUTOMATIC_ON_OFF
200 })
201 public @interface StartFlags {}
202
203 /**
junyulai4c95b082018-12-27 17:25:29 +0800204 * The minimum interval in seconds between keepalive packet transmissions.
205 *
206 * @hide
207 **/
208 public static final int MIN_INTERVAL_SEC = 10;
209
210 /**
211 * The maximum interval in seconds between keepalive packet transmissions.
212 *
213 * @hide
214 **/
215 public static final int MAX_INTERVAL_SEC = 3600;
216
junyulai011b1f12019-01-03 18:50:15 +0800217 /**
markchiene5591ce2018-12-27 22:49:51 +0800218 * An exception that embarks an error code.
219 * @hide
220 */
221 public static class ErrorCodeException extends Exception {
222 public final int error;
223 public ErrorCodeException(final int error, final Throwable e) {
224 super(e);
225 this.error = error;
226 }
227 public ErrorCodeException(final int error) {
228 this.error = error;
229 }
230 }
231
232 /**
233 * This socket is invalid.
234 * See the error code for details, and the optional cause.
235 * @hide
236 */
237 public static class InvalidSocketException extends ErrorCodeException {
238 public InvalidSocketException(final int error, final Throwable e) {
239 super(error, e);
240 }
241 public InvalidSocketException(final int error) {
242 super(error);
243 }
244 }
245
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900246 /** @hide */
247 @NonNull protected final IConnectivityManager mService;
248 /** @hide */
249 @NonNull protected final Network mNetwork;
250 /** @hide */
251 @NonNull protected final ParcelFileDescriptor mPfd;
252 /** @hide */
253 @NonNull protected final Executor mExecutor;
254 /** @hide */
255 @NonNull protected final ISocketKeepaliveCallback mCallback;
junyulai4c95b082018-12-27 17:25:29 +0800256
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900257 /** @hide */
258 public SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
junyulai7e06ad42019-03-04 22:45:36 +0800259 @NonNull ParcelFileDescriptor pfd,
junyulai4c95b082018-12-27 17:25:29 +0800260 @NonNull Executor executor, @NonNull Callback callback) {
261 mService = service;
262 mNetwork = network;
junyulai7e06ad42019-03-04 22:45:36 +0800263 mPfd = pfd;
junyulai4c95b082018-12-27 17:25:29 +0800264 mExecutor = executor;
junyulai070f9ff2019-01-16 20:23:34 +0800265 mCallback = new ISocketKeepaliveCallback.Stub() {
junyulai4c95b082018-12-27 17:25:29 +0800266 @Override
Chalard Jeanf0b261e2023-02-03 22:11:20 +0900267 public void onStarted() {
lucaslinbe801382020-12-30 11:54:55 +0800268 final long token = Binder.clearCallingIdentity();
269 try {
270 mExecutor.execute(() -> {
lucaslinbe801382020-12-30 11:54:55 +0800271 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
Chalard Jeanbdb82822023-01-19 23:14:02 +0900279 public void onResumed() {
280 final long token = Binder.clearCallingIdentity();
281 try {
282 mExecutor.execute(() -> {
283 callback.onResumed();
284 });
285 } finally {
286 Binder.restoreCallingIdentity(token);
287 }
288 }
289
290 @Override
junyulai070f9ff2019-01-16 20:23:34 +0800291 public void onStopped() {
lucaslinbe801382020-12-30 11:54:55 +0800292 final long token = Binder.clearCallingIdentity();
293 try {
294 executor.execute(() -> {
lucaslinbe801382020-12-30 11:54:55 +0800295 callback.onStopped();
296 });
297 } finally {
298 Binder.restoreCallingIdentity(token);
299 }
junyulai070f9ff2019-01-16 20:23:34 +0800300 }
301
302 @Override
Chalard Jeanbdb82822023-01-19 23:14:02 +0900303 public void onPaused() {
304 final long token = Binder.clearCallingIdentity();
305 try {
306 executor.execute(() -> {
307 callback.onPaused();
308 });
309 } finally {
310 Binder.restoreCallingIdentity(token);
311 }
312 }
313
314 @Override
junyulai070f9ff2019-01-16 20:23:34 +0800315 public void onError(int error) {
lucaslinbe801382020-12-30 11:54:55 +0800316 final long token = Binder.clearCallingIdentity();
317 try {
318 executor.execute(() -> {
lucaslinbe801382020-12-30 11:54:55 +0800319 callback.onError(error);
320 });
321 } finally {
322 Binder.restoreCallingIdentity(token);
323 }
junyulai070f9ff2019-01-16 20:23:34 +0800324 }
325
326 @Override
327 public void onDataReceived() {
lucaslinbe801382020-12-30 11:54:55 +0800328 final long token = Binder.clearCallingIdentity();
329 try {
330 executor.execute(() -> {
lucaslinbe801382020-12-30 11:54:55 +0800331 callback.onDataReceived();
332 });
333 } finally {
334 Binder.restoreCallingIdentity(token);
335 }
junyulai070f9ff2019-01-16 20:23:34 +0800336 }
337 };
junyulai4c95b082018-12-27 17:25:29 +0800338 }
339
340 /**
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000341 * Request that keepalive be started with the given {@code intervalSec}.
342 *
343 * See {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an
344 * exception when invoking start or stop of the {@link SocketKeepalive}, a
345 * {@link RuntimeException} caused by a {@link RemoteException} will be thrown into the
346 * {@link Executor}. This is typically not important to catch because the remote party is
347 * the system, so if it is not in shape to communicate through binder the system is going
348 * down anyway. If the caller still cares, it can use a custom {@link Executor} to catch the
349 * {@link RuntimeException}.
junyulai4c95b082018-12-27 17:25:29 +0800350 *
351 * @param intervalSec The target interval in seconds between keepalive packet transmissions.
352 * The interval should be between 10 seconds and 3600 seconds, otherwise
353 * {@link #ERROR_INVALID_INTERVAL} will be returned.
354 */
355 public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
356 int intervalSec) {
chiachangwang676c84e2023-02-14 09:22:05 +0000357 startImpl(intervalSec, 0 /* flags */, null /* underpinnedNetwork */);
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000358 }
359
360 /**
361 * Request that keepalive be started with the given {@code intervalSec}.
362 *
363 * See {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an
364 * exception when invoking start or stop of the {@link SocketKeepalive}, a
365 * {@link RuntimeException} caused by a {@link RemoteException} will be thrown into the
366 * {@link Executor}. This is typically not important to catch because the remote party is
367 * the system, so if it is not in shape to communicate through binder the system is going
368 * down anyway. If the caller still cares, it can use a custom {@link Executor} to catch the
369 * {@link RuntimeException}.
370 *
371 * @param intervalSec The target interval in seconds between keepalive packet transmissions.
372 * The interval should be between 10 seconds and 3600 seconds. Otherwise,
373 * the supplied {@link Callback} will see a call to
374 * {@link Callback#onError(int)} with {@link #ERROR_INVALID_INTERVAL}.
375 * @param flags Flags to enable/disable available options on this keepalive.
chiachangwangc51a7052023-03-13 02:25:44 +0000376 * @param underpinnedNetwork an optional network running over mNetwork that this
377 * keepalive is intended to keep up, e.g. an IPSec
378 * tunnel running over mNetwork.
chiachangwang9ef4ffe2023-01-18 01:19:27 +0000379 * @hide
380 */
381 @SystemApi(client = PRIVILEGED_APPS)
382 public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
chiachangwangc51a7052023-03-13 02:25:44 +0000383 int intervalSec, @StartFlags int flags, @Nullable Network underpinnedNetwork) {
chiachangwang676c84e2023-02-14 09:22:05 +0000384 startImpl(intervalSec, flags, underpinnedNetwork);
junyulai4c95b082018-12-27 17:25:29 +0800385 }
386
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900387 /** @hide */
chiachangwang676c84e2023-02-14 09:22:05 +0000388 protected abstract void startImpl(int intervalSec, @StartFlags int flags,
389 Network underpinnedNetwork);
junyulai4c95b082018-12-27 17:25:29 +0800390
junyulai4c95b082018-12-27 17:25:29 +0800391 /**
392 * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
393 * before using the object. See {@link SocketKeepalive}.
394 */
395 public final void stop() {
396 stopImpl();
397 }
398
Remi NGUYEN VANe55a88d2022-04-20 15:59:16 +0900399 /** @hide */
400 protected abstract void stopImpl();
junyulai4c95b082018-12-27 17:25:29 +0800401
402 /**
403 * Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be
404 * usable again if {@code close()} is called.
405 */
406 @Override
407 public final void close() {
408 stop();
junyulai7e06ad42019-03-04 22:45:36 +0800409 try {
410 mPfd.close();
411 } catch (IOException e) {
412 // Nothing much can be done.
413 }
junyulai4c95b082018-12-27 17:25:29 +0800414 }
415
416 /**
417 * The callback which app can use to learn the status changes of {@link SocketKeepalive}. See
418 * {@link SocketKeepalive}.
419 */
420 public static class Callback {
421 /** The requested keepalive was successfully started. */
422 public void onStarted() {}
Chalard Jeanbdb82822023-01-19 23:14:02 +0900423 /**
424 * The keepalive was resumed by the system after being suspended.
425 * @hide
426 **/
427 public void onResumed() {}
junyulai4c95b082018-12-27 17:25:29 +0800428 /** The keepalive was successfully stopped. */
429 public void onStopped() {}
Chalard Jeanbdb82822023-01-19 23:14:02 +0900430 /**
431 * The keepalive was paused by the system because it's not necessary right now.
432 * @hide
433 **/
434 public void onPaused() {}
junyulai4c95b082018-12-27 17:25:29 +0800435 /** An error occurred. */
436 public void onError(@ErrorCode int error) {}
junyulai070f9ff2019-01-16 20:23:34 +0800437 /** The keepalive on a TCP socket was stopped because the socket received data. This is
438 * never called for UDP sockets. */
junyulai4c95b082018-12-27 17:25:29 +0800439 public void onDataReceived() {}
440 }
441}