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.os.Binder; 24 import android.os.ParcelFileDescriptor; 25 import android.os.RemoteException; 26 27 import java.io.IOException; 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.util.concurrent.Executor; 31 32 /** 33 * Allows applications to request that the system periodically send specific packets on their 34 * behalf, using hardware offload to save battery power. 35 * 36 * To request that the system send keepalives, call one of the methods that return a 37 * {@link SocketKeepalive} object, such as {@link ConnectivityManager#createSocketKeepalive}, 38 * passing in a non-null callback. If the {@link SocketKeepalive} is successfully 39 * started, the callback's {@code onStarted} method will be called. If an error occurs, 40 * {@code onError} will be called, specifying one of the {@code ERROR_*} constants in this 41 * class. 42 * 43 * To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call 44 * {@link SocketKeepalive.Callback#onStopped} if the operation was successful or 45 * {@link SocketKeepalive.Callback#onError} if an error occurred. 46 * 47 * For cellular, the device MUST support at least 1 keepalive slot. 48 * 49 * For WiFi, the device SHOULD support keepalive offload. If it does not, it MUST reply with 50 * {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload 51 * request. If it does, it MUST support at least 3 concurrent keepalive slots. 52 */ 53 public abstract class SocketKeepalive implements AutoCloseable { 54 static final String TAG = "SocketKeepalive"; 55 56 /** @hide */ 57 public static final int SUCCESS = 0; 58 59 /** @hide */ 60 public static final int NO_KEEPALIVE = -1; 61 62 /** @hide */ 63 public static final int DATA_RECEIVED = -2; 64 65 /** @hide */ 66 public static final int BINDER_DIED = -10; 67 68 /** The specified {@code Network} is not connected. */ 69 public static final int ERROR_INVALID_NETWORK = -20; 70 /** The specified IP addresses are invalid. For example, the specified source IP address is 71 * not configured on the specified {@code Network}. */ 72 public static final int ERROR_INVALID_IP_ADDRESS = -21; 73 /** The requested port is invalid. */ 74 public static final int ERROR_INVALID_PORT = -22; 75 /** The packet length is invalid (e.g., too long). */ 76 public static final int ERROR_INVALID_LENGTH = -23; 77 /** The packet transmission interval is invalid (e.g., too short). */ 78 public static final int ERROR_INVALID_INTERVAL = -24; 79 /** The target socket is invalid. */ 80 public static final int ERROR_INVALID_SOCKET = -25; 81 /** The target socket is not idle. */ 82 public static final int ERROR_SOCKET_NOT_IDLE = -26; 83 84 /** The device does not support this request. */ 85 public static final int ERROR_UNSUPPORTED = -30; 86 /** @hide TODO: delete when telephony code has been updated. */ 87 public static final int ERROR_HARDWARE_UNSUPPORTED = ERROR_UNSUPPORTED; 88 /** The hardware returned an error. */ 89 public static final int ERROR_HARDWARE_ERROR = -31; 90 /** The limitation of resource is reached. */ 91 public static final int ERROR_INSUFFICIENT_RESOURCES = -32; 92 93 94 /** @hide */ 95 @Retention(RetentionPolicy.SOURCE) 96 @IntDef(prefix = { "ERROR_" }, value = { 97 ERROR_INVALID_NETWORK, 98 ERROR_INVALID_IP_ADDRESS, 99 ERROR_INVALID_PORT, 100 ERROR_INVALID_LENGTH, 101 ERROR_INVALID_INTERVAL, 102 ERROR_INVALID_SOCKET, 103 ERROR_SOCKET_NOT_IDLE 104 }) 105 public @interface ErrorCode {} 106 107 /** 108 * The minimum interval in seconds between keepalive packet transmissions. 109 * 110 * @hide 111 **/ 112 public static final int MIN_INTERVAL_SEC = 10; 113 114 /** 115 * The maximum interval in seconds between keepalive packet transmissions. 116 * 117 * @hide 118 **/ 119 public static final int MAX_INTERVAL_SEC = 3600; 120 121 /** 122 * An exception that embarks an error code. 123 * @hide 124 */ 125 public static class ErrorCodeException extends Exception { 126 public final int error; ErrorCodeException(final int error, final Throwable e)127 public ErrorCodeException(final int error, final Throwable e) { 128 super(e); 129 this.error = error; 130 } ErrorCodeException(final int error)131 public ErrorCodeException(final int error) { 132 this.error = error; 133 } 134 } 135 136 /** 137 * This socket is invalid. 138 * See the error code for details, and the optional cause. 139 * @hide 140 */ 141 public static class InvalidSocketException extends ErrorCodeException { InvalidSocketException(final int error, final Throwable e)142 public InvalidSocketException(final int error, final Throwable e) { 143 super(error, e); 144 } InvalidSocketException(final int error)145 public InvalidSocketException(final int error) { 146 super(error); 147 } 148 } 149 150 /** 151 * This packet is invalid. 152 * See the error code for details. 153 * @hide 154 */ 155 public static class InvalidPacketException extends ErrorCodeException { InvalidPacketException(final int error)156 public InvalidPacketException(final int error) { 157 super(error); 158 } 159 } 160 161 @NonNull final IConnectivityManager mService; 162 @NonNull final Network mNetwork; 163 @NonNull final ParcelFileDescriptor mPfd; 164 @NonNull final Executor mExecutor; 165 @NonNull final ISocketKeepaliveCallback mCallback; 166 // TODO: remove slot since mCallback could be used to identify which keepalive to stop. 167 @Nullable Integer mSlot; 168 SocketKeepalive(@onNull IConnectivityManager service, @NonNull Network network, @NonNull ParcelFileDescriptor pfd, @NonNull Executor executor, @NonNull Callback callback)169 SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network, 170 @NonNull ParcelFileDescriptor pfd, 171 @NonNull Executor executor, @NonNull Callback callback) { 172 mService = service; 173 mNetwork = network; 174 mPfd = pfd; 175 mExecutor = executor; 176 mCallback = new ISocketKeepaliveCallback.Stub() { 177 @Override 178 public void onStarted(int slot) { 179 Binder.withCleanCallingIdentity(() -> 180 mExecutor.execute(() -> { 181 mSlot = slot; 182 callback.onStarted(); 183 })); 184 } 185 186 @Override 187 public void onStopped() { 188 Binder.withCleanCallingIdentity(() -> 189 executor.execute(() -> { 190 mSlot = null; 191 callback.onStopped(); 192 })); 193 } 194 195 @Override 196 public void onError(int error) { 197 Binder.withCleanCallingIdentity(() -> 198 executor.execute(() -> { 199 mSlot = null; 200 callback.onError(error); 201 })); 202 } 203 204 @Override 205 public void onDataReceived() { 206 Binder.withCleanCallingIdentity(() -> 207 executor.execute(() -> { 208 mSlot = null; 209 callback.onDataReceived(); 210 })); 211 } 212 }; 213 } 214 215 /** 216 * Request that keepalive be started with the given {@code intervalSec}. See 217 * {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an exception 218 * when invoking start or stop of the {@link SocketKeepalive}, a {@link RemoteException} will be 219 * thrown into the {@code executor}. This is typically not important to catch because the remote 220 * party is the system, so if it is not in shape to communicate through binder the system is 221 * probably going down anyway. If the caller cares regardless, it can use a custom 222 * {@link Executor} to catch the {@link RemoteException}. 223 * 224 * @param intervalSec The target interval in seconds between keepalive packet transmissions. 225 * The interval should be between 10 seconds and 3600 seconds, otherwise 226 * {@link #ERROR_INVALID_INTERVAL} will be returned. 227 */ start(@ntRangefrom = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC) int intervalSec)228 public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC) 229 int intervalSec) { 230 startImpl(intervalSec); 231 } 232 startImpl(int intervalSec)233 abstract void startImpl(int intervalSec); 234 235 /** 236 * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped} 237 * before using the object. See {@link SocketKeepalive}. 238 */ stop()239 public final void stop() { 240 stopImpl(); 241 } 242 stopImpl()243 abstract void stopImpl(); 244 245 /** 246 * Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be 247 * usable again if {@code close()} is called. 248 */ 249 @Override close()250 public final void close() { 251 stop(); 252 try { 253 mPfd.close(); 254 } catch (IOException e) { 255 // Nothing much can be done. 256 } 257 } 258 259 /** 260 * The callback which app can use to learn the status changes of {@link SocketKeepalive}. See 261 * {@link SocketKeepalive}. 262 */ 263 public static class Callback { 264 /** The requested keepalive was successfully started. */ onStarted()265 public void onStarted() {} 266 /** The keepalive was successfully stopped. */ onStopped()267 public void onStopped() {} 268 /** An error occurred. */ onError(@rrorCode int error)269 public void onError(@ErrorCode int error) {} 270 /** The keepalive on a TCP socket was stopped because the socket received data. This is 271 * never called for UDP sockets. */ onDataReceived()272 public void onDataReceived() {} 273 } 274 } 275