Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2014 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.content.Context;
     20 import android.os.Bundle;
     21 import android.os.Handler;
     22 import android.os.Looper;
     23 import android.os.Message;
     24 import android.os.Messenger;
     25 import android.util.Log;
     26 
     27 import com.android.internal.util.AsyncChannel;
     28 import com.android.internal.util.Protocol;
     29 import android.net.ConnectivityManager.PacketKeepalive;
     30 
     31 import java.util.ArrayList;
     32 import java.util.concurrent.atomic.AtomicBoolean;
     33 
     34 /**
     35  * A Utility class for handling for communicating between bearer-specific
     36  * code and ConnectivityService.
     37  *
     38  * A bearer may have more than one NetworkAgent if it can simultaneously
     39  * support separate networks (IMS / Internet / MMS Apns on cellular, or
     40  * perhaps connections with different SSID or P2P for Wi-Fi).
     41  *
     42  * @hide
     43  */
     44 public abstract class NetworkAgent extends Handler {
     45     // Guaranteed to be valid (not NETID_UNSET), otherwise registerNetworkAgent() would have thrown
     46     // an exception.
     47     public final int netId;
     48 
     49     private volatile AsyncChannel mAsyncChannel;
     50     private final String LOG_TAG;
     51     private static final boolean DBG = true;
     52     private static final boolean VDBG = false;
     53     private final Context mContext;
     54     private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
     55     private volatile long mLastBwRefreshTime = 0;
     56     private static final long BW_REFRESH_MIN_WIN_MS = 500;
     57     private boolean mPollLceScheduled = false;
     58     private AtomicBoolean mPollLcePending = new AtomicBoolean(false);
     59 
     60     private static final int BASE = Protocol.BASE_NETWORK_AGENT;
     61 
     62     /**
     63      * Sent by ConnectivityService to the NetworkAgent to inform it of
     64      * suspected connectivity problems on its network.  The NetworkAgent
     65      * should take steps to verify and correct connectivity.
     66      */
     67     public static final int CMD_SUSPECT_BAD = BASE;
     68 
     69     /**
     70      * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to
     71      * ConnectivityService to pass the current NetworkInfo (connection state).
     72      * Sent when the NetworkInfo changes, mainly due to change of state.
     73      * obj = NetworkInfo
     74      */
     75     public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;
     76 
     77     /**
     78      * Sent by the NetworkAgent to ConnectivityService to pass the current
     79      * NetworkCapabilties.
     80      * obj = NetworkCapabilities
     81      */
     82     public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;
     83 
     84     /**
     85      * Sent by the NetworkAgent to ConnectivityService to pass the current
     86      * NetworkProperties.
     87      * obj = NetworkProperties
     88      */
     89     public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
     90 
     91     /* centralize place where base network score, and network score scaling, will be
     92      * stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE
     93      */
     94     public static final int WIFI_BASE_SCORE = 60;
     95 
     96     /**
     97      * Sent by the NetworkAgent to ConnectivityService to pass the current
     98      * network score.
     99      * obj = network score Integer
    100      */
    101     public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
    102 
    103     /**
    104      * Sent by the NetworkAgent to ConnectivityService to add new UID ranges
    105      * to be forced into this Network.  For VPNs only.
    106      * obj = UidRange[] to forward
    107      */
    108     public static final int EVENT_UID_RANGES_ADDED = BASE + 5;
    109 
    110     /**
    111      * Sent by the NetworkAgent to ConnectivityService to remove UID ranges
    112      * from being forced into this Network.  For VPNs only.
    113      * obj = UidRange[] to stop forwarding
    114      */
    115     public static final int EVENT_UID_RANGES_REMOVED = BASE + 6;
    116 
    117     /**
    118      * Sent by ConnectivityService to the NetworkAgent to inform the agent of the
    119      * networks status - whether we could use the network or could not, due to
    120      * either a bad network configuration (no internet link) or captive portal.
    121      *
    122      * arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK}
    123      * obj = Bundle containing map from {@code REDIRECT_URL_KEY} to {@code String}
    124      *       representing URL that Internet probe was redirect to, if it was redirected,
    125      *       or mapping to {@code null} otherwise.
    126      */
    127     public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
    128 
    129     public static final int VALID_NETWORK = 1;
    130     public static final int INVALID_NETWORK = 2;
    131 
    132     public static String REDIRECT_URL_KEY = "redirect URL";
    133 
    134      /**
    135      * Sent by the NetworkAgent to ConnectivityService to indicate this network was
    136      * explicitly selected.  This should be sent before the NetworkInfo is marked
    137      * CONNECTED so it can be given special treatment at that time.
    138      *
    139      * obj = boolean indicating whether to use this network even if unvalidated
    140      */
    141     public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8;
    142 
    143     /**
    144      * Sent by ConnectivityService to the NetworkAgent to inform the agent of
    145      * whether the network should in the future be used even if not validated.
    146      * This decision is made by the user, but it is the network transport's
    147      * responsibility to remember it.
    148      *
    149      * arg1 = 1 if true, 0 if false
    150      */
    151     public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
    152 
    153     /**
    154      * Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
    155      * the underlying network connection for updated bandwidth information.
    156      */
    157     public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
    158 
    159     /**
    160      * Sent by ConnectivityService to the NetworkAgent to request that the specified packet be sent
    161      * periodically on the given interval.
    162      *
    163      *   arg1 = the slot number of the keepalive to start
    164      *   arg2 = interval in seconds
    165      *   obj = KeepalivePacketData object describing the data to be sent
    166      *
    167      * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
    168      */
    169     public static final int CMD_START_PACKET_KEEPALIVE = BASE + 11;
    170 
    171     /**
    172      * Requests that the specified keepalive packet be stopped.
    173      *
    174      * arg1 = slot number of the keepalive to stop.
    175      *
    176      * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
    177      */
    178     public static final int CMD_STOP_PACKET_KEEPALIVE = BASE + 12;
    179 
    180     /**
    181      * Sent by the NetworkAgent to ConnectivityService to provide status on a packet keepalive
    182      * request. This may either be the reply to a CMD_START_PACKET_KEEPALIVE, or an asynchronous
    183      * error notification.
    184      *
    185      * This is also sent by KeepaliveTracker to the app's ConnectivityManager.PacketKeepalive to
    186      * so that the app's PacketKeepaliveCallback methods can be called.
    187      *
    188      * arg1 = slot number of the keepalive
    189      * arg2 = error code
    190      */
    191     public static final int EVENT_PACKET_KEEPALIVE = BASE + 13;
    192 
    193     /**
    194      * Sent by ConnectivityService to inform this network transport of signal strength thresholds
    195      * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
    196      *
    197      *   obj = int[] describing signal strength thresholds.
    198      */
    199     public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14;
    200 
    201     /**
    202      * Sent by ConnectivityService to the NeworkAgent to inform the agent to avoid
    203      * automatically reconnecting to this network (e.g. via autojoin).  Happens
    204      * when user selects "No" option on the "Stay connected?" dialog box.
    205      */
    206     public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
    207 
    208     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
    209             NetworkCapabilities nc, LinkProperties lp, int score) {
    210         this(looper, context, logTag, ni, nc, lp, score, null);
    211     }
    212 
    213     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
    214             NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
    215         super(looper);
    216         LOG_TAG = logTag;
    217         mContext = context;
    218         if (ni == null || nc == null || lp == null) {
    219             throw new IllegalArgumentException();
    220         }
    221 
    222         if (VDBG) log("Registering NetworkAgent");
    223         ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
    224                 Context.CONNECTIVITY_SERVICE);
    225         netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
    226                 new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);
    227     }
    228 
    229     @Override
    230     public void handleMessage(Message msg) {
    231         switch (msg.what) {
    232             case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
    233                 if (mAsyncChannel != null) {
    234                     log("Received new connection while already connected!");
    235                 } else {
    236                     if (VDBG) log("NetworkAgent fully connected");
    237                     AsyncChannel ac = new AsyncChannel();
    238                     ac.connected(null, this, msg.replyTo);
    239                     ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
    240                             AsyncChannel.STATUS_SUCCESSFUL);
    241                     synchronized (mPreConnectedQueue) {
    242                         mAsyncChannel = ac;
    243                         for (Message m : mPreConnectedQueue) {
    244                             ac.sendMessage(m);
    245                         }
    246                         mPreConnectedQueue.clear();
    247                     }
    248                 }
    249                 break;
    250             }
    251             case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
    252                 if (VDBG) log("CMD_CHANNEL_DISCONNECT");
    253                 if (mAsyncChannel != null) mAsyncChannel.disconnect();
    254                 break;
    255             }
    256             case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
    257                 if (DBG) log("NetworkAgent channel lost");
    258                 // let the client know CS is done with us.
    259                 unwanted();
    260                 synchronized (mPreConnectedQueue) {
    261                     mAsyncChannel = null;
    262                 }
    263                 break;
    264             }
    265             case CMD_SUSPECT_BAD: {
    266                 log("Unhandled Message " + msg);
    267                 break;
    268             }
    269             case CMD_REQUEST_BANDWIDTH_UPDATE: {
    270                 long currentTimeMs = System.currentTimeMillis();
    271                 if (VDBG) {
    272                     log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
    273                 }
    274                 if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
    275                     mPollLceScheduled = false;
    276                     if (mPollLcePending.getAndSet(true) == false) {
    277                         pollLceData();
    278                     }
    279                 } else {
    280                     // deliver the request at a later time rather than discard it completely.
    281                     if (!mPollLceScheduled) {
    282                         long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS -
    283                                 currentTimeMs + 1;
    284                         mPollLceScheduled = sendEmptyMessageDelayed(
    285                                 CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
    286                     }
    287                 }
    288                 break;
    289             }
    290             case CMD_REPORT_NETWORK_STATUS: {
    291                 String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
    292                 if (VDBG) {
    293                     log("CMD_REPORT_NETWORK_STATUS(" +
    294                             (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
    295                 }
    296                 networkStatus(msg.arg1, redirectUrl);
    297                 break;
    298             }
    299             case CMD_SAVE_ACCEPT_UNVALIDATED: {
    300                 saveAcceptUnvalidated(msg.arg1 != 0);
    301                 break;
    302             }
    303             case CMD_START_PACKET_KEEPALIVE: {
    304                 startPacketKeepalive(msg);
    305                 break;
    306             }
    307             case CMD_STOP_PACKET_KEEPALIVE: {
    308                 stopPacketKeepalive(msg);
    309                 break;
    310             }
    311 
    312             case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
    313                 ArrayList<Integer> thresholds =
    314                         ((Bundle) msg.obj).getIntegerArrayList("thresholds");
    315                 // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
    316                 // rather than convert to int[].
    317                 int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
    318                 for (int i = 0; i < intThresholds.length; i++) {
    319                     intThresholds[i] = thresholds.get(i);
    320                 }
    321                 setSignalStrengthThresholds(intThresholds);
    322                 break;
    323             }
    324             case CMD_PREVENT_AUTOMATIC_RECONNECT: {
    325                 preventAutomaticReconnect();
    326                 break;
    327             }
    328         }
    329     }
    330 
    331     private void queueOrSendMessage(int what, Object obj) {
    332         queueOrSendMessage(what, 0, 0, obj);
    333     }
    334 
    335     private void queueOrSendMessage(int what, int arg1, int arg2) {
    336         queueOrSendMessage(what, arg1, arg2, null);
    337     }
    338 
    339     private void queueOrSendMessage(int what, int arg1, int arg2, Object obj) {
    340         Message msg = Message.obtain();
    341         msg.what = what;
    342         msg.arg1 = arg1;
    343         msg.arg2 = arg2;
    344         msg.obj = obj;
    345         queueOrSendMessage(msg);
    346     }
    347 
    348     private void queueOrSendMessage(Message msg) {
    349         synchronized (mPreConnectedQueue) {
    350             if (mAsyncChannel != null) {
    351                 mAsyncChannel.sendMessage(msg);
    352             } else {
    353                 mPreConnectedQueue.add(msg);
    354             }
    355         }
    356     }
    357 
    358     /**
    359      * Called by the bearer code when it has new LinkProperties data.
    360      */
    361     public void sendLinkProperties(LinkProperties linkProperties) {
    362         queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
    363     }
    364 
    365     /**
    366      * Called by the bearer code when it has new NetworkInfo data.
    367      */
    368     public void sendNetworkInfo(NetworkInfo networkInfo) {
    369         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
    370     }
    371 
    372     /**
    373      * Called by the bearer code when it has new NetworkCapabilities data.
    374      */
    375     public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
    376         mPollLcePending.set(false);
    377         mLastBwRefreshTime = System.currentTimeMillis();
    378         queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
    379                 new NetworkCapabilities(networkCapabilities));
    380     }
    381 
    382     /**
    383      * Called by the bearer code when it has a new score for this network.
    384      */
    385     public void sendNetworkScore(int score) {
    386         if (score < 0) {
    387             throw new IllegalArgumentException("Score must be >= 0");
    388         }
    389         queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new Integer(score));
    390     }
    391 
    392     /**
    393      * Called by the VPN code when it wants to add ranges of UIDs to be routed
    394      * through the VPN network.
    395      */
    396     public void addUidRanges(UidRange[] ranges) {
    397         queueOrSendMessage(EVENT_UID_RANGES_ADDED, ranges);
    398     }
    399 
    400     /**
    401      * Called by the VPN code when it wants to remove ranges of UIDs from being routed
    402      * through the VPN network.
    403      */
    404     public void removeUidRanges(UidRange[] ranges) {
    405         queueOrSendMessage(EVENT_UID_RANGES_REMOVED, ranges);
    406     }
    407 
    408     /**
    409      * Called by the bearer to indicate this network was manually selected by the user.
    410      * This should be called before the NetworkInfo is marked CONNECTED so that this
    411      * Network can be given special treatment at that time. If {@code acceptUnvalidated} is
    412      * {@code true}, then the system will switch to this network. If it is {@code false} and the
    413      * network cannot be validated, the system will ask the user whether to switch to this network.
    414      * If the user confirms and selects "don't ask again", then the system will call
    415      * {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever
    416      * calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement
    417      * {@link #saveAcceptUnvalidated} to respect the user's choice.
    418      */
    419     public void explicitlySelected(boolean acceptUnvalidated) {
    420         queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, acceptUnvalidated);
    421     }
    422 
    423     /**
    424      * Called when ConnectivityService has indicated they no longer want this network.
    425      * The parent factory should (previously) have received indication of the change
    426      * as well, either canceling NetworkRequests or altering their score such that this
    427      * network won't be immediately requested again.
    428      */
    429     abstract protected void unwanted();
    430 
    431     /**
    432      * Called when ConnectivityService request a bandwidth update. The parent factory
    433      * shall try to overwrite this method and produce a bandwidth update if capable.
    434      */
    435     protected void pollLceData() {
    436     }
    437 
    438     /**
    439      * Called when the system determines the usefulness of this network.
    440      *
    441      * Networks claiming internet connectivity will have their internet
    442      * connectivity verified.
    443      *
    444      * Currently there are two possible values:
    445      * {@code VALID_NETWORK} if the system is happy with the connection,
    446      * {@code INVALID_NETWORK} if the system is not happy.
    447      * TODO - add indications of captive portal-ness and related success/failure,
    448      * ie, CAPTIVE_SUCCESS_NETWORK, CAPTIVE_NETWORK for successful login and detection
    449      *
    450      * This may be called multiple times as the network status changes and may
    451      * generate false negatives if we lose ip connectivity before the link is torn down.
    452      *
    453      * @param status one of {@code VALID_NETWORK} or {@code INVALID_NETWORK}.
    454      * @param redirectUrl If the Internet probe was redirected, this is the destination it was
    455      *         redirected to, otherwise {@code null}.
    456      */
    457     protected void networkStatus(int status, String redirectUrl) {
    458     }
    459 
    460     /**
    461      * Called when the user asks to remember the choice to use this network even if unvalidated.
    462      * The transport is responsible for remembering the choice, and the next time the user connects
    463      * to the network, should explicitlySelected with {@code acceptUnvalidated} set to {@code true}.
    464      * This method will only be called if {@link #explicitlySelected} was called with
    465      * {@code acceptUnvalidated} set to {@code false}.
    466      */
    467     protected void saveAcceptUnvalidated(boolean accept) {
    468     }
    469 
    470     /**
    471      * Requests that the network hardware send the specified packet at the specified interval.
    472      */
    473     protected void startPacketKeepalive(Message msg) {
    474         onPacketKeepaliveEvent(msg.arg1, PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
    475     }
    476 
    477     /**
    478      * Requests that the network hardware send the specified packet at the specified interval.
    479      */
    480     protected void stopPacketKeepalive(Message msg) {
    481         onPacketKeepaliveEvent(msg.arg1, PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
    482     }
    483 
    484     /**
    485      * Called by the network when a packet keepalive event occurs.
    486      */
    487     public void onPacketKeepaliveEvent(int slot, int reason) {
    488         queueOrSendMessage(EVENT_PACKET_KEEPALIVE, slot, reason);
    489     }
    490 
    491     /**
    492      * Called by ConnectivityService to inform this network transport of signal strength thresholds
    493      * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
    494      */
    495     protected void setSignalStrengthThresholds(int[] thresholds) {
    496     }
    497 
    498     /**
    499      * Called when the user asks to not stay connected to this network because it was found to not
    500      * provide Internet access.  Usually followed by call to {@code unwanted}.  The transport is
    501      * responsible for making sure the device does not automatically reconnect to the same network
    502      * after the {@code unwanted} call.
    503      */
    504     protected void preventAutomaticReconnect() {
    505     }
    506 
    507     protected void log(String s) {
    508         Log.d(LOG_TAG, "NetworkAgent: " + s);
    509     }
    510 }
    511