1 /*
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 
17 package android.net;
18 
19 import static android.annotation.SystemApi.Client.PRIVILEGED_APPS;
20 
21 import android.annotation.IntDef;
22 import android.annotation.IntRange;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.SystemApi;
26 import android.os.Binder;
27 import android.os.ParcelFileDescriptor;
28 import android.os.RemoteException;
29 
30 import java.io.IOException;
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.RetentionPolicy;
33 import 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.
49  *
50  * 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
53  * {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload
54  * request. If it does, it MUST support at least 3 concurrent keepalive slots.
55  */
56 public abstract class SocketKeepalive implements AutoCloseable {
57     /** @hide */
58     protected static final String TAG = "SocketKeepalive";
59 
60     /**
61      * Success. It indicates there is no error.
62      * @hide
63      */
64     @SystemApi
65     public static final int SUCCESS = 0;
66 
67     /**
68      * Success when trying to suspend.
69      * @hide
70      */
71     public static final int SUCCESS_PAUSED = 1;
72 
73     /**
74      * No keepalive. This should only be internally as it indicates There is no keepalive.
75      * It should not propagate to applications.
76      * @hide
77      */
78     public static final int NO_KEEPALIVE = -1;
79 
80     /**
81      * Data received.
82      * @hide
83      */
84     public static final int DATA_RECEIVED = -2;
85 
86     /**
87      * The binder died.
88      * @hide
89      */
90     public static final int BINDER_DIED = -10;
91 
92     /**
93      * The invalid network. It indicates the specified {@code Network} is not connected.
94      */
95     public static final int ERROR_INVALID_NETWORK = -20;
96 
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      */
102     public static final int ERROR_INVALID_IP_ADDRESS = -21;
103 
104     /**
105      * The port is invalid.
106      */
107     public static final int ERROR_INVALID_PORT = -22;
108 
109     /**
110      * The length is invalid (e.g. too long).
111      */
112     public static final int ERROR_INVALID_LENGTH = -23;
113 
114     /**
115      * The interval is invalid (e.g. too short).
116      */
117     public static final int ERROR_INVALID_INTERVAL = -24;
118 
119     /**
120      * The socket is invalid.
121      */
122     public static final int ERROR_INVALID_SOCKET = -25;
123 
124     /**
125      * The socket is not idle.
126      */
127     public static final int ERROR_SOCKET_NOT_IDLE = -26;
128 
129     /**
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;
135 
136     /**
137      * The request is unsupported.
138      */
139     public static final int ERROR_UNSUPPORTED = -30;
140 
141     /**
142      * There was a hardware error.
143      */
144     public static final int ERROR_HARDWARE_ERROR = -31;
145 
146     /**
147      * Resources are insufficient (e.g. all hardware slots are in use).
148      */
149     public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
150 
151     /**
152      * There was no such slot, or no keepalive running on this slot.
153      * @hide
154      */
155     @SystemApi
156     public static final int ERROR_NO_SUCH_SLOT = -33;
157 
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,
167             ERROR_SOCKET_NOT_IDLE,
168             ERROR_NO_SUCH_SLOT
169     })
170     public @interface ErrorCode {}
171 
172     /** @hide */
173     @Retention(RetentionPolicy.SOURCE)
174     @IntDef(value = {
175             SUCCESS,
176             ERROR_INVALID_LENGTH,
177             ERROR_UNSUPPORTED,
178             ERROR_INSUFFICIENT_RESOURCES,
179     })
180     public @interface KeepaliveEvent {}
181 
182     /**
183      * 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     /**
204      * 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 
217     /**
218      * An exception that embarks an error code.
219      * @hide
220      */
221     public static class ErrorCodeException extends Exception {
222         public final int error;
ErrorCodeException(final int error, final Throwable e)223         public ErrorCodeException(final int error, final Throwable e) {
224             super(e);
225             this.error = error;
226         }
ErrorCodeException(final int error)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 {
InvalidSocketException(final int error, final Throwable e)238         public InvalidSocketException(final int error, final Throwable e) {
239             super(error, e);
240         }
InvalidSocketException(final int error)241         public InvalidSocketException(final int error) {
242             super(error);
243         }
244     }
245 
246     /** @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;
256 
257     /** @hide */
SocketKeepalive(@onNull IConnectivityManager service, @NonNull Network network, @NonNull ParcelFileDescriptor pfd, @NonNull Executor executor, @NonNull Callback callback)258     public SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
259             @NonNull ParcelFileDescriptor pfd,
260             @NonNull Executor executor, @NonNull Callback callback) {
261         mService = service;
262         mNetwork = network;
263         mPfd = pfd;
264         mExecutor = executor;
265         mCallback = new ISocketKeepaliveCallback.Stub() {
266             @Override
267             public void onStarted() {
268                 final long token = Binder.clearCallingIdentity();
269                 try {
270                     mExecutor.execute(() -> {
271                         callback.onStarted();
272                     });
273                 } finally {
274                     Binder.restoreCallingIdentity(token);
275                 }
276             }
277 
278             @Override
279             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
291             public void onStopped() {
292                 final long token = Binder.clearCallingIdentity();
293                 try {
294                     executor.execute(() -> {
295                         callback.onStopped();
296                     });
297                 } finally {
298                     Binder.restoreCallingIdentity(token);
299                 }
300             }
301 
302             @Override
303             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
315             public void onError(int error) {
316                 final long token = Binder.clearCallingIdentity();
317                 try {
318                     executor.execute(() -> {
319                         callback.onError(error);
320                     });
321                 } finally {
322                     Binder.restoreCallingIdentity(token);
323                 }
324             }
325 
326             @Override
327             public void onDataReceived() {
328                 final long token = Binder.clearCallingIdentity();
329                 try {
330                     executor.execute(() -> {
331                         callback.onDataReceived();
332                     });
333                 } finally {
334                     Binder.restoreCallingIdentity(token);
335                 }
336             }
337         };
338     }
339 
340     /**
341      * 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}.
350      *
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      */
start(@ntRangefrom = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC) int intervalSec)355     public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
356             int intervalSec) {
357         startImpl(intervalSec, 0 /* flags */, null /* underpinnedNetwork */);
358     }
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.
376      * @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.
379      * @hide
380      */
381     @SystemApi(client = PRIVILEGED_APPS)
start(@ntRangefrom = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC) int intervalSec, @StartFlags int flags, @Nullable Network underpinnedNetwork)382     public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
383             int intervalSec, @StartFlags int flags, @Nullable Network underpinnedNetwork) {
384         startImpl(intervalSec, flags, underpinnedNetwork);
385     }
386 
387     /** @hide */
startImpl(int intervalSec, @StartFlags int flags, Network underpinnedNetwork)388     protected abstract void startImpl(int intervalSec, @StartFlags int flags,
389             Network underpinnedNetwork);
390 
391     /**
392      * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
393      * before using the object. See {@link SocketKeepalive}.
394      */
stop()395     public final void stop() {
396         stopImpl();
397     }
398 
399     /** @hide */
stopImpl()400     protected abstract void stopImpl();
401 
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
close()407     public final void close() {
408         stop();
409         try {
410             mPfd.close();
411         } catch (IOException e) {
412             // Nothing much can be done.
413         }
414     }
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. */
onStarted()422         public void onStarted() {}
423         /**
424          * The keepalive was resumed by the system after being suspended.
425          * @hide
426          **/
onResumed()427         public void onResumed() {}
428         /** The keepalive was successfully stopped. */
onStopped()429         public void onStopped() {}
430         /**
431          * The keepalive was paused by the system because it's not necessary right now.
432          * @hide
433          **/
onPaused()434         public void onPaused() {}
435         /** An error occurred. */
onError(@rrorCode int error)436         public void onError(@ErrorCode int error) {}
437         /** The keepalive on a TCP socket was stopped because the socket received data. This is
438          * never called for UDP sockets. */
onDataReceived()439         public void onDataReceived() {}
440     }
441 }
442