Home | History | Annotate | Download | only in net
      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;
    127         public ErrorCodeException(final int error, final Throwable e) {
    128             super(e);
    129             this.error = error;
    130         }
    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 {
    142         public InvalidSocketException(final int error, final Throwable e) {
    143             super(error, e);
    144         }
    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 {
    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 
    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      */
    228     public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
    229             int intervalSec) {
    230         startImpl(intervalSec);
    231     }
    232 
    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      */
    239     public final void stop() {
    240         stopImpl();
    241     }
    242 
    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
    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. */
    265         public void onStarted() {}
    266         /** The keepalive was successfully stopped. */
    267         public void onStopped() {}
    268         /** An error occurred. */
    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. */
    272         public void onDataReceived() {}
    273     }
    274 }
    275