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 * 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 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; 93 /** The hardware returned an error. */ 94 public static final int ERROR_HARDWARE_ERROR = -31; 95 /** The limitation of resource is reached. */ 96 public static final int ERROR_INSUFFICIENT_RESOURCES = -32; 97 98 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 112 /** @hide */ 113 @Retention(RetentionPolicy.SOURCE) 114 @IntDef(value = { 115 SUCCESS, 116 ERROR_INVALID_LENGTH, 117 ERROR_UNSUPPORTED, 118 ERROR_INSUFFICIENT_RESOURCES, 119 ERROR_HARDWARE_UNSUPPORTED 120 }) 121 public @interface KeepaliveEvent {} 122 123 /** 124 * The minimum interval in seconds between keepalive packet transmissions. 125 * 126 * @hide 127 **/ 128 public static final int MIN_INTERVAL_SEC = 10; 129 130 /** 131 * The maximum interval in seconds between keepalive packet transmissions. 132 * 133 * @hide 134 **/ 135 public static final int MAX_INTERVAL_SEC = 3600; 136 137 /** 138 * An exception that embarks an error code. 139 * @hide 140 */ 141 public static class ErrorCodeException extends Exception { 142 public final int error; ErrorCodeException(final int error, final Throwable e)143 public ErrorCodeException(final int error, final Throwable e) { 144 super(e); 145 this.error = error; 146 } ErrorCodeException(final int error)147 public ErrorCodeException(final int error) { 148 this.error = error; 149 } 150 } 151 152 /** 153 * This socket is invalid. 154 * See the error code for details, and the optional cause. 155 * @hide 156 */ 157 public static class InvalidSocketException extends ErrorCodeException { InvalidSocketException(final int error, final Throwable e)158 public InvalidSocketException(final int error, final Throwable e) { 159 super(error, e); 160 } InvalidSocketException(final int error)161 public InvalidSocketException(final int error) { 162 super(error); 163 } 164 } 165 166 @NonNull final IConnectivityManager mService; 167 @NonNull final Network mNetwork; 168 @NonNull final ParcelFileDescriptor mPfd; 169 @NonNull final Executor mExecutor; 170 @NonNull final ISocketKeepaliveCallback mCallback; 171 // TODO: remove slot since mCallback could be used to identify which keepalive to stop. 172 @Nullable Integer mSlot; 173 SocketKeepalive(@onNull IConnectivityManager service, @NonNull Network network, @NonNull ParcelFileDescriptor pfd, @NonNull Executor executor, @NonNull Callback callback)174 SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network, 175 @NonNull ParcelFileDescriptor pfd, 176 @NonNull Executor executor, @NonNull Callback callback) { 177 mService = service; 178 mNetwork = network; 179 mPfd = pfd; 180 mExecutor = executor; 181 mCallback = new ISocketKeepaliveCallback.Stub() { 182 @Override 183 public void onStarted(int slot) { 184 Binder.withCleanCallingIdentity(() -> 185 mExecutor.execute(() -> { 186 mSlot = slot; 187 callback.onStarted(); 188 })); 189 } 190 191 @Override 192 public void onStopped() { 193 Binder.withCleanCallingIdentity(() -> 194 executor.execute(() -> { 195 mSlot = null; 196 callback.onStopped(); 197 })); 198 } 199 200 @Override 201 public void onError(int error) { 202 Binder.withCleanCallingIdentity(() -> 203 executor.execute(() -> { 204 mSlot = null; 205 callback.onError(error); 206 })); 207 } 208 209 @Override 210 public void onDataReceived() { 211 Binder.withCleanCallingIdentity(() -> 212 executor.execute(() -> { 213 mSlot = null; 214 callback.onDataReceived(); 215 })); 216 } 217 }; 218 } 219 220 /** 221 * Request that keepalive be started with the given {@code intervalSec}. See 222 * {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an exception 223 * when invoking start or stop of the {@link SocketKeepalive}, a {@link RemoteException} will be 224 * thrown into the {@code executor}. This is typically not important to catch because the remote 225 * party is the system, so if it is not in shape to communicate through binder the system is 226 * probably going down anyway. If the caller cares regardless, it can use a custom 227 * {@link Executor} to catch the {@link RemoteException}. 228 * 229 * @param intervalSec The target interval in seconds between keepalive packet transmissions. 230 * The interval should be between 10 seconds and 3600 seconds, otherwise 231 * {@link #ERROR_INVALID_INTERVAL} will be returned. 232 */ start(@ntRangefrom = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC) int intervalSec)233 public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC) 234 int intervalSec) { 235 startImpl(intervalSec); 236 } 237 startImpl(int intervalSec)238 abstract void startImpl(int intervalSec); 239 240 /** 241 * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped} 242 * before using the object. See {@link SocketKeepalive}. 243 */ stop()244 public final void stop() { 245 stopImpl(); 246 } 247 stopImpl()248 abstract void stopImpl(); 249 250 /** 251 * Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be 252 * usable again if {@code close()} is called. 253 */ 254 @Override close()255 public final void close() { 256 stop(); 257 try { 258 mPfd.close(); 259 } catch (IOException e) { 260 // Nothing much can be done. 261 } 262 } 263 264 /** 265 * The callback which app can use to learn the status changes of {@link SocketKeepalive}. See 266 * {@link SocketKeepalive}. 267 */ 268 public static class Callback { 269 /** The requested keepalive was successfully started. */ onStarted()270 public void onStarted() {} 271 /** The keepalive was successfully stopped. */ onStopped()272 public void onStopped() {} 273 /** An error occurred. */ onError(@rrorCode int error)274 public void onError(@ErrorCode int error) {} 275 /** The keepalive on a TCP socket was stopped because the socket received data. This is 276 * never called for UDP sockets. */ onDataReceived()277 public void onDataReceived() {} 278 } 279 } 280