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