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 
NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score)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 
NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc)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
handleMessage(Message msg)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 
queueOrSendMessage(int what, Object obj)331     private void queueOrSendMessage(int what, Object obj) {
332         queueOrSendMessage(what, 0, 0, obj);
333     }
334 
queueOrSendMessage(int what, int arg1, int arg2)335     private void queueOrSendMessage(int what, int arg1, int arg2) {
336         queueOrSendMessage(what, arg1, arg2, null);
337     }
338 
queueOrSendMessage(int what, int arg1, int arg2, Object obj)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 
queueOrSendMessage(Message msg)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      */
sendLinkProperties(LinkProperties linkProperties)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      */
sendNetworkInfo(NetworkInfo networkInfo)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      */
sendNetworkCapabilities(NetworkCapabilities networkCapabilities)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      */
sendNetworkScore(int score)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      */
addUidRanges(UidRange[] ranges)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      */
removeUidRanges(UidRange[] ranges)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      */
explicitlySelected(boolean acceptUnvalidated)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      */
unwanted()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      */
pollLceData()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      */
networkStatus(int status, String redirectUrl)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      */
saveAcceptUnvalidated(boolean accept)467     protected void saveAcceptUnvalidated(boolean accept) {
468     }
469 
470     /**
471      * Requests that the network hardware send the specified packet at the specified interval.
472      */
startPacketKeepalive(Message msg)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      */
stopPacketKeepalive(Message msg)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      */
onPacketKeepaliveEvent(int slot, int reason)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      */
setSignalStrengthThresholds(int[] thresholds)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      */
preventAutomaticReconnect()504     protected void preventAutomaticReconnect() {
505     }
506 
log(String s)507     protected void log(String s) {
508         Log.d(LOG_TAG, "NetworkAgent: " + s);
509     }
510 }
511