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 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 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 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 317 private void queueOrSendMessage(int what, Object obj) { 318 queueOrSendMessage(what, 0, 0, obj); 319 } 320 321 private void queueOrSendMessage(int what, int arg1, int arg2) { 322 queueOrSendMessage(what, arg1, arg2, null); 323 } 324 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 437 protected void saveAcceptUnvalidated(boolean accept) { 438 } 439 440 /** 441 * Requests that the network hardware send the specified packet at the specified interval. 442 */ 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 */ 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 */ 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 */ 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 */ 474 protected void preventAutomaticReconnect() { 475 } 476 477 protected void log(String s) { 478 Log.d(LOG_TAG, "NetworkAgent: " + s); 479 } 480 } 481