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