blob: d007a9520cb58d1682088839c9df2b47fada5bc4 [file] [log] [blame]
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +09001/*
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;
22import android.annotation.Nullable;
23import android.annotation.SystemApi;
24import android.os.Binder;
25import android.os.ParcelFileDescriptor;
26import android.os.RemoteException;
27
28import java.io.IOException;
29import 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.
47 *
48 * 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
51 * {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload
52 * request. If it does, it MUST support at least 3 concurrent keepalive slots.
53 */
54public abstract class SocketKeepalive implements AutoCloseable {
55 static final String TAG = "SocketKeepalive";
56
57 /**
58 * No errors.
59 * @hide
60 */
61 @SystemApi
62 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 /**
89 * The stop reason is uninitialized. This should only be internally used as initial state
90 * of stop reason, instead of propagating to application.
91 * @hide
92 */
93 public static final int ERROR_STOP_REASON_UNINITIALIZED = -27;
94
95 /** The device does not support this request. */
96 public static final int ERROR_UNSUPPORTED = -30;
97 /** @hide TODO: delete when telephony code has been updated. */
98 public static final int ERROR_HARDWARE_UNSUPPORTED = ERROR_UNSUPPORTED;
99 /** The hardware returned an error. */
100 public static final int ERROR_HARDWARE_ERROR = -31;
101 /** The limitation of resource is reached. */
102 public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
103
104
105 /** @hide */
106 @Retention(RetentionPolicy.SOURCE)
107 @IntDef(prefix = { "ERROR_" }, value = {
108 ERROR_INVALID_NETWORK,
109 ERROR_INVALID_IP_ADDRESS,
110 ERROR_INVALID_PORT,
111 ERROR_INVALID_LENGTH,
112 ERROR_INVALID_INTERVAL,
113 ERROR_INVALID_SOCKET,
114 ERROR_SOCKET_NOT_IDLE
115 })
116 public @interface ErrorCode {}
117
118 /** @hide */
119 @Retention(RetentionPolicy.SOURCE)
120 @IntDef(value = {
121 SUCCESS,
122 ERROR_INVALID_LENGTH,
123 ERROR_UNSUPPORTED,
124 ERROR_INSUFFICIENT_RESOURCES,
125 ERROR_HARDWARE_UNSUPPORTED
126 })
127 public @interface KeepaliveEvent {}
128
129 /**
130 * The minimum interval in seconds between keepalive packet transmissions.
131 *
132 * @hide
133 **/
134 public static final int MIN_INTERVAL_SEC = 10;
135
136 /**
137 * The maximum interval in seconds between keepalive packet transmissions.
138 *
139 * @hide
140 **/
141 public static final int MAX_INTERVAL_SEC = 3600;
142
143 /**
144 * An exception that embarks an error code.
145 * @hide
146 */
147 public static class ErrorCodeException extends Exception {
148 public final int error;
149 public ErrorCodeException(final int error, final Throwable e) {
150 super(e);
151 this.error = error;
152 }
153 public ErrorCodeException(final int error) {
154 this.error = error;
155 }
156 }
157
158 /**
159 * This socket is invalid.
160 * See the error code for details, and the optional cause.
161 * @hide
162 */
163 public static class InvalidSocketException extends ErrorCodeException {
164 public InvalidSocketException(final int error, final Throwable e) {
165 super(error, e);
166 }
167 public InvalidSocketException(final int error) {
168 super(error);
169 }
170 }
171
172 @NonNull final IConnectivityManager mService;
173 @NonNull final Network mNetwork;
174 @NonNull final ParcelFileDescriptor mPfd;
175 @NonNull final Executor mExecutor;
176 @NonNull final ISocketKeepaliveCallback mCallback;
177 // TODO: remove slot since mCallback could be used to identify which keepalive to stop.
178 @Nullable Integer mSlot;
179
180 SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
181 @NonNull ParcelFileDescriptor pfd,
182 @NonNull Executor executor, @NonNull Callback callback) {
183 mService = service;
184 mNetwork = network;
185 mPfd = pfd;
186 mExecutor = executor;
187 mCallback = new ISocketKeepaliveCallback.Stub() {
188 @Override
189 public void onStarted(int slot) {
190 final long token = Binder.clearCallingIdentity();
191 try {
192 mExecutor.execute(() -> {
193 mSlot = slot;
194 callback.onStarted();
195 });
196 } finally {
197 Binder.restoreCallingIdentity(token);
198 }
199 }
200
201 @Override
202 public void onStopped() {
203 final long token = Binder.clearCallingIdentity();
204 try {
205 executor.execute(() -> {
206 mSlot = null;
207 callback.onStopped();
208 });
209 } finally {
210 Binder.restoreCallingIdentity(token);
211 }
212 }
213
214 @Override
215 public void onError(int error) {
216 final long token = Binder.clearCallingIdentity();
217 try {
218 executor.execute(() -> {
219 mSlot = null;
220 callback.onError(error);
221 });
222 } finally {
223 Binder.restoreCallingIdentity(token);
224 }
225 }
226
227 @Override
228 public void onDataReceived() {
229 final long token = Binder.clearCallingIdentity();
230 try {
231 executor.execute(() -> {
232 mSlot = null;
233 callback.onDataReceived();
234 });
235 } finally {
236 Binder.restoreCallingIdentity(token);
237 }
238 }
239 };
240 }
241
242 /**
243 * Request that keepalive be started with the given {@code intervalSec}. See
244 * {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an exception
245 * when invoking start or stop of the {@link SocketKeepalive}, a {@link RemoteException} will be
246 * thrown into the {@code executor}. This is typically not important to catch because the remote
247 * party is the system, so if it is not in shape to communicate through binder the system is
248 * probably going down anyway. If the caller cares regardless, it can use a custom
249 * {@link Executor} to catch the {@link RemoteException}.
250 *
251 * @param intervalSec The target interval in seconds between keepalive packet transmissions.
252 * The interval should be between 10 seconds and 3600 seconds, otherwise
253 * {@link #ERROR_INVALID_INTERVAL} will be returned.
254 */
255 public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
256 int intervalSec) {
257 startImpl(intervalSec);
258 }
259
260 abstract void startImpl(int intervalSec);
261
262 /**
263 * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
264 * before using the object. See {@link SocketKeepalive}.
265 */
266 public final void stop() {
267 stopImpl();
268 }
269
270 abstract void stopImpl();
271
272 /**
273 * Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be
274 * usable again if {@code close()} is called.
275 */
276 @Override
277 public final void close() {
278 stop();
279 try {
280 mPfd.close();
281 } catch (IOException e) {
282 // Nothing much can be done.
283 }
284 }
285
286 /**
287 * The callback which app can use to learn the status changes of {@link SocketKeepalive}. See
288 * {@link SocketKeepalive}.
289 */
290 public static class Callback {
291 /** The requested keepalive was successfully started. */
292 public void onStarted() {}
293 /** The keepalive was successfully stopped. */
294 public void onStopped() {}
295 /** An error occurred. */
296 public void onError(@ErrorCode int error) {}
297 /** The keepalive on a TCP socket was stopped because the socket received data. This is
298 * never called for UDP sockets. */
299 public void onDataReceived() {}
300 }
301}