• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.annotation.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.os.Binder;
25 import android.os.ParcelFileDescriptor;
26 import android.os.RemoteException;
27 
28 import java.io.IOException;
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import 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  */
54 public abstract class SocketKeepalive implements AutoCloseable {
55     static final String TAG = "SocketKeepalive";
56 
57     /**
58      * Success. It indicates there is no error.
59      * @hide
60      */
61     @SystemApi
62     public static final int SUCCESS = 0;
63 
64     /**
65      * No keepalive. This should only be internally as it indicates There is no keepalive.
66      * It should not propagate to applications.
67      * @hide
68      */
69     public static final int NO_KEEPALIVE = -1;
70 
71     /**
72      * Data received.
73      * @hide
74      */
75     public static final int DATA_RECEIVED = -2;
76 
77     /**
78      * The binder died.
79      * @hide
80      */
81     public static final int BINDER_DIED = -10;
82 
83     /**
84      * The invalid network. It indicates the specified {@code Network} is not connected.
85      */
86     public static final int ERROR_INVALID_NETWORK = -20;
87 
88     /**
89      * The invalid IP addresses. Indicates the specified IP addresses are invalid.
90      * For example, the specified source IP address is not configured on the
91      * specified {@code Network}.
92      */
93     public static final int ERROR_INVALID_IP_ADDRESS = -21;
94 
95     /**
96      * The port is invalid.
97      */
98     public static final int ERROR_INVALID_PORT = -22;
99 
100     /**
101      * The length is invalid (e.g. too long).
102      */
103     public static final int ERROR_INVALID_LENGTH = -23;
104 
105     /**
106      * The interval is invalid (e.g. too short).
107      */
108     public static final int ERROR_INVALID_INTERVAL = -24;
109 
110     /**
111      * The socket is invalid.
112      */
113     public static final int ERROR_INVALID_SOCKET = -25;
114 
115     /**
116      * The socket is not idle.
117      */
118     public static final int ERROR_SOCKET_NOT_IDLE = -26;
119 
120     /**
121      * The stop reason is uninitialized. This should only be internally used as initial state
122      * of stop reason, instead of propagating to application.
123      * @hide
124      */
125     public static final int ERROR_STOP_REASON_UNINITIALIZED = -27;
126 
127     /**
128      * The request is unsupported.
129      */
130     public static final int ERROR_UNSUPPORTED = -30;
131 
132     /**
133      * There was a hardware error.
134      */
135     public static final int ERROR_HARDWARE_ERROR = -31;
136 
137     /**
138      * Resources are insufficient (e.g. all hardware slots are in use).
139      */
140     public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
141 
142     /**
143      * There was no such slot. This should only be internally as it indicates
144      * a programming error in the system server. It should not propagate to
145      * applications.
146      * @hide
147      */
148     @SystemApi
149     public static final int ERROR_NO_SUCH_SLOT = -33;
150 
151     /** @hide */
152     @Retention(RetentionPolicy.SOURCE)
153     @IntDef(prefix = { "ERROR_" }, value = {
154             ERROR_INVALID_NETWORK,
155             ERROR_INVALID_IP_ADDRESS,
156             ERROR_INVALID_PORT,
157             ERROR_INVALID_LENGTH,
158             ERROR_INVALID_INTERVAL,
159             ERROR_INVALID_SOCKET,
160             ERROR_SOCKET_NOT_IDLE,
161             ERROR_NO_SUCH_SLOT
162     })
163     public @interface ErrorCode {}
164 
165     /** @hide */
166     @Retention(RetentionPolicy.SOURCE)
167     @IntDef(value = {
168             SUCCESS,
169             ERROR_INVALID_LENGTH,
170             ERROR_UNSUPPORTED,
171             ERROR_INSUFFICIENT_RESOURCES,
172     })
173     public @interface KeepaliveEvent {}
174 
175     /**
176      * The minimum interval in seconds between keepalive packet transmissions.
177      *
178      * @hide
179      **/
180     public static final int MIN_INTERVAL_SEC = 10;
181 
182     /**
183      * The maximum interval in seconds between keepalive packet transmissions.
184      *
185      * @hide
186      **/
187     public static final int MAX_INTERVAL_SEC = 3600;
188 
189     /**
190      * An exception that embarks an error code.
191      * @hide
192      */
193     public static class ErrorCodeException extends Exception {
194         public final int error;
ErrorCodeException(final int error, final Throwable e)195         public ErrorCodeException(final int error, final Throwable e) {
196             super(e);
197             this.error = error;
198         }
ErrorCodeException(final int error)199         public ErrorCodeException(final int error) {
200             this.error = error;
201         }
202     }
203 
204     /**
205      * This socket is invalid.
206      * See the error code for details, and the optional cause.
207      * @hide
208      */
209     public static class InvalidSocketException extends ErrorCodeException {
InvalidSocketException(final int error, final Throwable e)210         public InvalidSocketException(final int error, final Throwable e) {
211             super(error, e);
212         }
InvalidSocketException(final int error)213         public InvalidSocketException(final int error) {
214             super(error);
215         }
216     }
217 
218     @NonNull final IConnectivityManager mService;
219     @NonNull final Network mNetwork;
220     @NonNull final ParcelFileDescriptor mPfd;
221     @NonNull final Executor mExecutor;
222     @NonNull final ISocketKeepaliveCallback mCallback;
223     // TODO: remove slot since mCallback could be used to identify which keepalive to stop.
224     @Nullable Integer mSlot;
225 
SocketKeepalive(@onNull IConnectivityManager service, @NonNull Network network, @NonNull ParcelFileDescriptor pfd, @NonNull Executor executor, @NonNull Callback callback)226     SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
227             @NonNull ParcelFileDescriptor pfd,
228             @NonNull Executor executor, @NonNull Callback callback) {
229         mService = service;
230         mNetwork = network;
231         mPfd = pfd;
232         mExecutor = executor;
233         mCallback = new ISocketKeepaliveCallback.Stub() {
234             @Override
235             public void onStarted(int slot) {
236                 final long token = Binder.clearCallingIdentity();
237                 try {
238                     mExecutor.execute(() -> {
239                         mSlot = slot;
240                         callback.onStarted();
241                     });
242                 } finally {
243                     Binder.restoreCallingIdentity(token);
244                 }
245             }
246 
247             @Override
248             public void onStopped() {
249                 final long token = Binder.clearCallingIdentity();
250                 try {
251                     executor.execute(() -> {
252                         mSlot = null;
253                         callback.onStopped();
254                     });
255                 } finally {
256                     Binder.restoreCallingIdentity(token);
257                 }
258             }
259 
260             @Override
261             public void onError(int error) {
262                 final long token = Binder.clearCallingIdentity();
263                 try {
264                     executor.execute(() -> {
265                         mSlot = null;
266                         callback.onError(error);
267                     });
268                 } finally {
269                     Binder.restoreCallingIdentity(token);
270                 }
271             }
272 
273             @Override
274             public void onDataReceived() {
275                 final long token = Binder.clearCallingIdentity();
276                 try {
277                     executor.execute(() -> {
278                         mSlot = null;
279                         callback.onDataReceived();
280                     });
281                 } finally {
282                     Binder.restoreCallingIdentity(token);
283                 }
284             }
285         };
286     }
287 
288     /**
289      * Request that keepalive be started with the given {@code intervalSec}. See
290      * {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an exception
291      * when invoking start or stop of the {@link SocketKeepalive}, a {@link RemoteException} will be
292      * thrown into the {@code executor}. This is typically not important to catch because the remote
293      * party is the system, so if it is not in shape to communicate through binder the system is
294      * probably going down anyway. If the caller cares regardless, it can use a custom
295      * {@link Executor} to catch the {@link RemoteException}.
296      *
297      * @param intervalSec The target interval in seconds between keepalive packet transmissions.
298      *                    The interval should be between 10 seconds and 3600 seconds, otherwise
299      *                    {@link #ERROR_INVALID_INTERVAL} will be returned.
300      */
start(@ntRangefrom = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC) int intervalSec)301     public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
302             int intervalSec) {
303         startImpl(intervalSec);
304     }
305 
startImpl(int intervalSec)306     abstract void startImpl(int intervalSec);
307 
308     /**
309      * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
310      * before using the object. See {@link SocketKeepalive}.
311      */
stop()312     public final void stop() {
313         stopImpl();
314     }
315 
stopImpl()316     abstract void stopImpl();
317 
318     /**
319      * Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be
320      * usable again if {@code close()} is called.
321      */
322     @Override
close()323     public final void close() {
324         stop();
325         try {
326             mPfd.close();
327         } catch (IOException e) {
328             // Nothing much can be done.
329         }
330     }
331 
332     /**
333      * The callback which app can use to learn the status changes of {@link SocketKeepalive}. See
334      * {@link SocketKeepalive}.
335      */
336     public static class Callback {
337         /** The requested keepalive was successfully started. */
onStarted()338         public void onStarted() {}
339         /** The keepalive was successfully stopped. */
onStopped()340         public void onStopped() {}
341         /** An error occurred. */
onError(@rrorCode int error)342         public void onError(@ErrorCode int error) {}
343         /** The keepalive on a TCP socket was stopped because the socket received data. This is
344          * never called for UDP sockets. */
onDataReceived()345         public void onDataReceived() {}
346     }
347 }
348