blob: 8ff8f4c48c32650790562ae4514db2e3fd53f83b [file] [log] [blame]
junyulai48eac1d42018-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
19import android.annotation.IntDef;
20import android.annotation.IntRange;
21import android.annotation.NonNull;
junyulai352dc2f2019-01-08 20:04:33 +080022import android.annotation.Nullable;
paulhubd47e8c2020-01-13 16:18:54 +080023import android.annotation.SystemApi;
junyulai7c469172019-01-16 20:23:34 +080024import android.os.Binder;
junyulai0c666972019-03-04 22:45:36 +080025import android.os.ParcelFileDescriptor;
junyulai7c469172019-01-16 20:23:34 +080026import android.os.RemoteException;
junyulai48eac1d42018-12-27 17:25:29 +080027
junyulai0c666972019-03-04 22:45:36 +080028import java.io.IOException;
junyulai48eac1d42018-12-27 17:25:29 +080029import java.lang.annotation.Retention;
30import java.lang.annotation.RetentionPolicy;
31import java.util.concurrent.Executor;
32
33/**
34 * Allows applications to request that the system periodically send specific packets on their
35 * behalf, using hardware offload to save battery power.
36 *
37 * To request that the system send keepalives, call one of the methods that return a
38 * {@link SocketKeepalive} object, such as {@link ConnectivityManager#createSocketKeepalive},
39 * passing in a non-null callback. If the {@link SocketKeepalive} is successfully
40 * started, the callback's {@code onStarted} method will be called. If an error occurs,
41 * {@code onError} will be called, specifying one of the {@code ERROR_*} constants in this
42 * class.
43 *
44 * To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call
45 * {@link SocketKeepalive.Callback#onStopped} if the operation was successful or
46 * {@link SocketKeepalive.Callback#onError} if an error occurred.
junyulaia91094a2019-04-17 15:22:46 +080047 *
Aaron Huangc0de2832019-06-05 17:09:29 +080048 * For cellular, the device MUST support at least 1 keepalive slot.
49 *
50 * For WiFi, the device SHOULD support keepalive offload. If it does not, it MUST reply with
junyulaia91094a2019-04-17 15:22:46 +080051 * {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload
Aaron Huangc0de2832019-06-05 17:09:29 +080052 * request. If it does, it MUST support at least 3 concurrent keepalive slots.
junyulai48eac1d42018-12-27 17:25:29 +080053 */
54public abstract class SocketKeepalive implements AutoCloseable {
55 static final String TAG = "SocketKeepalive";
56
paulhubd47e8c2020-01-13 16:18:54 +080057 /**
58 * No errors.
59 * @hide
60 */
61 @SystemApi
junyulai48eac1d42018-12-27 17:25:29 +080062 public static final int SUCCESS = 0;
63
64 /** @hide */
65 public static final int NO_KEEPALIVE = -1;
66
67 /** @hide */
68 public static final int DATA_RECEIVED = -2;
69
70 /** @hide */
71 public static final int BINDER_DIED = -10;
72
73 /** The specified {@code Network} is not connected. */
74 public static final int ERROR_INVALID_NETWORK = -20;
75 /** The specified IP addresses are invalid. For example, the specified source IP address is
76 * not configured on the specified {@code Network}. */
77 public static final int ERROR_INVALID_IP_ADDRESS = -21;
78 /** The requested port is invalid. */
79 public static final int ERROR_INVALID_PORT = -22;
80 /** The packet length is invalid (e.g., too long). */
81 public static final int ERROR_INVALID_LENGTH = -23;
82 /** The packet transmission interval is invalid (e.g., too short). */
83 public static final int ERROR_INVALID_INTERVAL = -24;
84 /** The target socket is invalid. */
85 public static final int ERROR_INVALID_SOCKET = -25;
86 /** The target socket is not idle. */
87 public static final int ERROR_SOCKET_NOT_IDLE = -26;
88
junyulai0c666972019-03-04 22:45:36 +080089 /** The device does not support this request. */
90 public static final int ERROR_UNSUPPORTED = -30;
91 /** @hide TODO: delete when telephony code has been updated. */
92 public static final int ERROR_HARDWARE_UNSUPPORTED = ERROR_UNSUPPORTED;
junyulai48eac1d42018-12-27 17:25:29 +080093 /** The hardware returned an error. */
94 public static final int ERROR_HARDWARE_ERROR = -31;
junyulai0c666972019-03-04 22:45:36 +080095 /** The limitation of resource is reached. */
96 public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
97
junyulai48eac1d42018-12-27 17:25:29 +080098
99 /** @hide */
100 @Retention(RetentionPolicy.SOURCE)
101 @IntDef(prefix = { "ERROR_" }, value = {
102 ERROR_INVALID_NETWORK,
103 ERROR_INVALID_IP_ADDRESS,
104 ERROR_INVALID_PORT,
105 ERROR_INVALID_LENGTH,
106 ERROR_INVALID_INTERVAL,
107 ERROR_INVALID_SOCKET,
108 ERROR_SOCKET_NOT_IDLE
109 })
110 public @interface ErrorCode {}
111
Chalard Jeanf1dec612020-03-24 23:16:35 +0900112 /** @hide */
113 @Retention(RetentionPolicy.SOURCE)
114 @IntDef(value = {
115 SUCCESS,
116 ERROR_INVALID_LENGTH,
117 ERROR_UNSUPPORTED,
118 ERROR_INSUFFICIENT_RESOURCES
119 })
120 public @interface KeepaliveEvent {}
121
junyulai48eac1d42018-12-27 17:25:29 +0800122 /**
123 * The minimum interval in seconds between keepalive packet transmissions.
124 *
125 * @hide
126 **/
127 public static final int MIN_INTERVAL_SEC = 10;
128
129 /**
130 * The maximum interval in seconds between keepalive packet transmissions.
131 *
132 * @hide
133 **/
134 public static final int MAX_INTERVAL_SEC = 3600;
135
junyulai06835112019-01-03 18:50:15 +0800136 /**
markchien150e1912018-12-27 22:49:51 +0800137 * An exception that embarks an error code.
138 * @hide
139 */
140 public static class ErrorCodeException extends Exception {
141 public final int error;
142 public ErrorCodeException(final int error, final Throwable e) {
143 super(e);
144 this.error = error;
145 }
146 public ErrorCodeException(final int error) {
147 this.error = error;
148 }
149 }
150
151 /**
152 * This socket is invalid.
153 * See the error code for details, and the optional cause.
154 * @hide
155 */
156 public static class InvalidSocketException extends ErrorCodeException {
157 public InvalidSocketException(final int error, final Throwable e) {
158 super(error, e);
159 }
160 public InvalidSocketException(final int error) {
161 super(error);
162 }
163 }
164
junyulai48eac1d42018-12-27 17:25:29 +0800165 @NonNull final IConnectivityManager mService;
166 @NonNull final Network mNetwork;
junyulai0c666972019-03-04 22:45:36 +0800167 @NonNull final ParcelFileDescriptor mPfd;
junyulai7c469172019-01-16 20:23:34 +0800168 @NonNull final Executor mExecutor;
169 @NonNull final ISocketKeepaliveCallback mCallback;
170 // TODO: remove slot since mCallback could be used to identify which keepalive to stop.
junyulai352dc2f2019-01-08 20:04:33 +0800171 @Nullable Integer mSlot;
junyulai48eac1d42018-12-27 17:25:29 +0800172
173 SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
junyulai0c666972019-03-04 22:45:36 +0800174 @NonNull ParcelFileDescriptor pfd,
junyulai48eac1d42018-12-27 17:25:29 +0800175 @NonNull Executor executor, @NonNull Callback callback) {
176 mService = service;
177 mNetwork = network;
junyulai0c666972019-03-04 22:45:36 +0800178 mPfd = pfd;
junyulai48eac1d42018-12-27 17:25:29 +0800179 mExecutor = executor;
junyulai7c469172019-01-16 20:23:34 +0800180 mCallback = new ISocketKeepaliveCallback.Stub() {
junyulai48eac1d42018-12-27 17:25:29 +0800181 @Override
junyulai7c469172019-01-16 20:23:34 +0800182 public void onStarted(int slot) {
183 Binder.withCleanCallingIdentity(() ->
184 mExecutor.execute(() -> {
185 mSlot = slot;
186 callback.onStarted();
187 }));
junyulai48eac1d42018-12-27 17:25:29 +0800188 }
junyulai7c469172019-01-16 20:23:34 +0800189
190 @Override
191 public void onStopped() {
192 Binder.withCleanCallingIdentity(() ->
193 executor.execute(() -> {
194 mSlot = null;
195 callback.onStopped();
196 }));
197 }
198
199 @Override
200 public void onError(int error) {
201 Binder.withCleanCallingIdentity(() ->
202 executor.execute(() -> {
203 mSlot = null;
204 callback.onError(error);
205 }));
206 }
207
208 @Override
209 public void onDataReceived() {
210 Binder.withCleanCallingIdentity(() ->
211 executor.execute(() -> {
212 mSlot = null;
213 callback.onDataReceived();
214 }));
215 }
216 };
junyulai48eac1d42018-12-27 17:25:29 +0800217 }
218
219 /**
220 * Request that keepalive be started with the given {@code intervalSec}. See
junyulai7c469172019-01-16 20:23:34 +0800221 * {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an exception
222 * when invoking start or stop of the {@link SocketKeepalive}, a {@link RemoteException} will be
223 * thrown into the {@code executor}. This is typically not important to catch because the remote
224 * party is the system, so if it is not in shape to communicate through binder the system is
225 * probably going down anyway. If the caller cares regardless, it can use a custom
226 * {@link Executor} to catch the {@link RemoteException}.
junyulai48eac1d42018-12-27 17:25:29 +0800227 *
228 * @param intervalSec The target interval in seconds between keepalive packet transmissions.
229 * The interval should be between 10 seconds and 3600 seconds, otherwise
230 * {@link #ERROR_INVALID_INTERVAL} will be returned.
231 */
232 public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
233 int intervalSec) {
234 startImpl(intervalSec);
235 }
236
237 abstract void startImpl(int intervalSec);
238
junyulai48eac1d42018-12-27 17:25:29 +0800239 /**
240 * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
241 * before using the object. See {@link SocketKeepalive}.
242 */
243 public final void stop() {
244 stopImpl();
245 }
246
247 abstract void stopImpl();
248
249 /**
250 * Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be
251 * usable again if {@code close()} is called.
252 */
253 @Override
254 public final void close() {
255 stop();
junyulai0c666972019-03-04 22:45:36 +0800256 try {
257 mPfd.close();
258 } catch (IOException e) {
259 // Nothing much can be done.
260 }
junyulai48eac1d42018-12-27 17:25:29 +0800261 }
262
263 /**
264 * The callback which app can use to learn the status changes of {@link SocketKeepalive}. See
265 * {@link SocketKeepalive}.
266 */
267 public static class Callback {
268 /** The requested keepalive was successfully started. */
269 public void onStarted() {}
270 /** The keepalive was successfully stopped. */
271 public void onStopped() {}
272 /** An error occurred. */
273 public void onError(@ErrorCode int error) {}
junyulai7c469172019-01-16 20:23:34 +0800274 /** The keepalive on a TCP socket was stopped because the socket received data. This is
275 * never called for UDP sockets. */
junyulai48eac1d42018-12-27 17:25:29 +0800276 public void onDataReceived() {}
277 }
278}