ConnectivityService.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
1/* 2 * Copyright (C) 2008 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 17package com.android.server; 18 19import android.app.Notification; 20import android.app.NotificationManager; 21import android.content.ContentResolver; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.PackageManager; 25import android.net.ConnectivityManager; 26import android.net.IConnectivityManager; 27import android.net.MobileDataStateTracker; 28import android.net.NetworkInfo; 29import android.net.NetworkStateTracker; 30import android.net.wifi.WifiStateTracker; 31import android.os.Binder; 32import android.os.Handler; 33import android.os.Looper; 34import android.os.Message; 35import android.os.ServiceManager; 36import android.os.SystemProperties; 37import android.provider.Settings; 38import android.util.EventLog; 39import android.util.Log; 40 41import java.io.FileDescriptor; 42import java.io.PrintWriter; 43 44/** 45 * @hide 46 */ 47public class ConnectivityService extends IConnectivityManager.Stub { 48 49 private static final boolean DBG = false; 50 private static final String TAG = "ConnectivityService"; 51 52 // Event log tags (must be in sync with event-log-tags) 53 private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020; 54 55 /** 56 * Sometimes we want to refer to the individual network state 57 * trackers separately, and sometimes we just want to treat them 58 * abstractly. 59 */ 60 private NetworkStateTracker mNetTrackers[]; 61 private WifiStateTracker mWifiStateTracker; 62 private MobileDataStateTracker mMobileDataStateTracker; 63 private WifiWatchdogService mWifiWatchdogService; 64 65 private Context mContext; 66 private int mNetworkPreference; 67 private NetworkStateTracker mActiveNetwork; 68 69 private int mNumDnsEntries; 70 private static int sDnsChangeCounter; 71 72 private boolean mTestMode; 73 private static ConnectivityService sServiceInstance; 74 75 private static class ConnectivityThread extends Thread { 76 private Context mContext; 77 78 private ConnectivityThread(Context context) { 79 super("ConnectivityThread"); 80 mContext = context; 81 } 82 83 @Override 84 public void run() { 85 Looper.prepare(); 86 synchronized (this) { 87 sServiceInstance = new ConnectivityService(mContext); 88 notifyAll(); 89 } 90 Looper.loop(); 91 } 92 93 public static ConnectivityService getServiceInstance(Context context) { 94 ConnectivityThread thread = new ConnectivityThread(context); 95 thread.start(); 96 97 synchronized (thread) { 98 while (sServiceInstance == null) { 99 try { 100 // Wait until sServiceInstance has been initialized. 101 thread.wait(); 102 } catch (InterruptedException ignore) { 103 Log.e(TAG, 104 "Unexpected InterruptedException while waiting for ConnectivityService thread"); 105 } 106 } 107 } 108 109 return sServiceInstance; 110 } 111 } 112 113 public static ConnectivityService getInstance(Context context) { 114 return ConnectivityThread.getServiceInstance(context); 115 } 116 117 private ConnectivityService(Context context) { 118 if (DBG) Log.v(TAG, "ConnectivityService starting up"); 119 mContext = context; 120 mNetTrackers = new NetworkStateTracker[2]; 121 Handler handler = new MyHandler(); 122 123 mNetworkPreference = getPersistedNetworkPreference(); 124 125 /* 126 * Create the network state trackers for Wi-Fi and mobile 127 * data. Maybe this could be done with a factory class, 128 * but it's not clear that it's worth it, given that 129 * the number of different network types is not going 130 * to change very often. 131 */ 132 if (DBG) Log.v(TAG, "Starting Wifi Service."); 133 mWifiStateTracker = new WifiStateTracker(context, handler); 134 WifiService wifiService = new WifiService(context, mWifiStateTracker); 135 ServiceManager.addService(Context.WIFI_SERVICE, wifiService); 136 mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker; 137 138 mMobileDataStateTracker = new MobileDataStateTracker(context, handler); 139 mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker; 140 141 mActiveNetwork = null; 142 mNumDnsEntries = 0; 143 144 mTestMode = SystemProperties.get("cm.test.mode").equals("true") 145 && SystemProperties.get("ro.build.type").equals("eng"); 146 147 for (NetworkStateTracker t : mNetTrackers) 148 t.startMonitoring(); 149 150 // Constructing this starts it too 151 mWifiWatchdogService = new WifiWatchdogService(context, mWifiStateTracker); 152 } 153 154 /** 155 * Sets the preferred network. 156 * @param preference the new preference 157 */ 158 public synchronized void setNetworkPreference(int preference) { 159 enforceChangePermission(); 160 if (ConnectivityManager.isNetworkTypeValid(preference)) { 161 if (mNetworkPreference != preference) { 162 persistNetworkPreference(preference); 163 mNetworkPreference = preference; 164 enforcePreference(); 165 } 166 } 167 } 168 169 public int getNetworkPreference() { 170 enforceAccessPermission(); 171 return mNetworkPreference; 172 } 173 174 private void persistNetworkPreference(int networkPreference) { 175 final ContentResolver cr = mContext.getContentResolver(); 176 Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, networkPreference); 177 } 178 179 private int getPersistedNetworkPreference() { 180 final ContentResolver cr = mContext.getContentResolver(); 181 182 final int networkPrefSetting = Settings.Secure 183 .getInt(cr, Settings.Secure.NETWORK_PREFERENCE, -1); 184 if (networkPrefSetting != -1) { 185 return networkPrefSetting; 186 } 187 188 return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE; 189 } 190 191 /** 192 * Make the state of network connectivity conform to the preference settings. 193 * In this method, we only tear down a non-preferred network. Establishing 194 * a connection to the preferred network is taken care of when we handle 195 * the disconnect event from the non-preferred network 196 * (see {@link #handleDisconnect(NetworkInfo)}). 197 */ 198 private void enforcePreference() { 199 if (mActiveNetwork == null) 200 return; 201 202 for (NetworkStateTracker t : mNetTrackers) { 203 if (t == mActiveNetwork) { 204 int netType = t.getNetworkInfo().getType(); 205 int otherNetType = ((netType == ConnectivityManager.TYPE_WIFI) ? 206 ConnectivityManager.TYPE_MOBILE : 207 ConnectivityManager.TYPE_WIFI); 208 209 if (t.getNetworkInfo().getType() != mNetworkPreference) { 210 NetworkStateTracker otherTracker = mNetTrackers[otherNetType]; 211 if (otherTracker.isAvailable()) { 212 teardown(t); 213 } 214 } 215 } 216 } 217 } 218 219 private boolean teardown(NetworkStateTracker netTracker) { 220 if (netTracker.teardown()) { 221 netTracker.setTeardownRequested(true); 222 return true; 223 } else { 224 return false; 225 } 226 } 227 228 /** 229 * Return NetworkInfo for the active (i.e., connected) network interface. 230 * It is assumed that at most one network is active at a time. If more 231 * than one is active, it is indeterminate which will be returned. 232 * @return the info for the active network, or {@code null} if none is active 233 */ 234 public NetworkInfo getActiveNetworkInfo() { 235 enforceAccessPermission(); 236 for (NetworkStateTracker t : mNetTrackers) { 237 NetworkInfo info = t.getNetworkInfo(); 238 if (info.isConnected()) { 239 return info; 240 } 241 } 242 return null; 243 } 244 245 public NetworkInfo getNetworkInfo(int networkType) { 246 enforceAccessPermission(); 247 if (ConnectivityManager.isNetworkTypeValid(networkType)) { 248 NetworkStateTracker t = mNetTrackers[networkType]; 249 if (t != null) 250 return t.getNetworkInfo(); 251 } 252 return null; 253 } 254 255 public NetworkInfo[] getAllNetworkInfo() { 256 enforceAccessPermission(); 257 NetworkInfo[] result = new NetworkInfo[mNetTrackers.length]; 258 int i = 0; 259 for (NetworkStateTracker t : mNetTrackers) { 260 result[i++] = t.getNetworkInfo(); 261 } 262 return result; 263 } 264 265 public boolean setRadios(boolean turnOn) { 266 boolean result = true; 267 enforceChangePermission(); 268 for (NetworkStateTracker t : mNetTrackers) { 269 result = t.setRadio(turnOn) && result; 270 } 271 return result; 272 } 273 274 public boolean setRadio(int netType, boolean turnOn) { 275 enforceChangePermission(); 276 if (!ConnectivityManager.isNetworkTypeValid(netType)) { 277 return false; 278 } 279 NetworkStateTracker tracker = mNetTrackers[netType]; 280 return tracker != null && tracker.setRadio(turnOn); 281 } 282 283 public int startUsingNetworkFeature(int networkType, String feature) { 284 enforceChangePermission(); 285 if (!ConnectivityManager.isNetworkTypeValid(networkType)) { 286 return -1; 287 } 288 NetworkStateTracker tracker = mNetTrackers[networkType]; 289 if (tracker != null) { 290 return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid()); 291 } 292 return -1; 293 } 294 295 public int stopUsingNetworkFeature(int networkType, String feature) { 296 enforceChangePermission(); 297 if (!ConnectivityManager.isNetworkTypeValid(networkType)) { 298 return -1; 299 } 300 NetworkStateTracker tracker = mNetTrackers[networkType]; 301 if (tracker != null) { 302 return tracker.stopUsingNetworkFeature(feature, getCallingPid(), getCallingUid()); 303 } 304 return -1; 305 } 306 307 /** 308 * Ensure that a network route exists to deliver traffic to the specified 309 * host via the specified network interface. 310 * @param networkType the type of the network over which traffic to the specified 311 * host is to be routed 312 * @param hostAddress the IP address of the host to which the route is desired 313 * @return {@code true} on success, {@code false} on failure 314 */ 315 public boolean requestRouteToHost(int networkType, int hostAddress) { 316 enforceChangePermission(); 317 if (!ConnectivityManager.isNetworkTypeValid(networkType)) { 318 return false; 319 } 320 NetworkStateTracker tracker = mNetTrackers[networkType]; 321 /* 322 * If there's only one connected network, and it's the one requested, 323 * then we don't have to do anything - the requested route already 324 * exists. If it's not the requested network, then it's not possible 325 * to establish the requested route. Finally, if there is more than 326 * one connected network, then we must insert an entry in the routing 327 * table. 328 */ 329 if (getNumConnectedNetworks() > 1) { 330 return tracker.requestRouteToHost(hostAddress); 331 } else { 332 return tracker.getNetworkInfo().getType() == networkType; 333 } 334 } 335 336 private int getNumConnectedNetworks() { 337 int numConnectedNets = 0; 338 339 for (NetworkStateTracker nt : mNetTrackers) { 340 if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) { 341 ++numConnectedNets; 342 } 343 } 344 return numConnectedNets; 345 } 346 347 private void enforceAccessPermission() { 348 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE, 349 "ConnectivityService"); 350 } 351 352 private void enforceChangePermission() { 353 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE, 354 "ConnectivityService"); 355 356 } 357 358 /** 359 * Handle a {@code DISCONNECTED} event. If this pertains to the non-active network, 360 * we ignore it. If it is for the active network, we send out a broadcast. 361 * But first, we check whether it might be possible to connect to a different 362 * network. 363 * @param info the {@code NetworkInfo} for the network 364 */ 365 private void handleDisconnect(NetworkInfo info) { 366 367 if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName()); 368 369 mNetTrackers[info.getType()].setTeardownRequested(false); 370 /* 371 * If the disconnected network is not the active one, then don't report 372 * this as a loss of connectivity. What probably happened is that we're 373 * getting the disconnect for a network that we explicitly disabled 374 * in accordance with network preference policies. 375 */ 376 if (mActiveNetwork == null || info.getType() != mActiveNetwork.getNetworkInfo().getType()) 377 return; 378 379 NetworkStateTracker newNet; 380 if (info.getType() == ConnectivityManager.TYPE_MOBILE) { 381 newNet = mWifiStateTracker; 382 } else /* info().getType() == TYPE_WIFI */ { 383 newNet = mMobileDataStateTracker; 384 } 385 386 /** 387 * See if the other network is available to fail over to. 388 * If is not available, we enable it anyway, so that it 389 * will be able to connect when it does become available, 390 * but we report a total loss of connectivity rather than 391 * report that we are attempting to fail over. 392 */ 393 NetworkInfo switchTo = null; 394 if (newNet.isAvailable()) { 395 mActiveNetwork = newNet; 396 switchTo = newNet.getNetworkInfo(); 397 switchTo.setFailover(true); 398 if (!switchTo.isConnectedOrConnecting()) { 399 newNet.reconnect(); 400 } 401 } else { 402 newNet.reconnect(); 403 } 404 405 boolean otherNetworkConnected = false; 406 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); 407 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); 408 if (info.isFailover()) { 409 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); 410 info.setFailover(false); 411 } 412 if (info.getReason() != null) { 413 intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason()); 414 } 415 if (info.getExtraInfo() != null) { 416 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo()); 417 } 418 if (switchTo != null) { 419 otherNetworkConnected = switchTo.isConnected(); 420 if (DBG) { 421 if (otherNetworkConnected) { 422 Log.v(TAG, "Switching to already connected " + switchTo.getTypeName()); 423 } else { 424 Log.v(TAG, "Attempting to switch to " + switchTo.getTypeName()); 425 } 426 } 427 intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo); 428 } else { 429 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); 430 } 431 if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() + 432 (switchTo == null ? "" : " other=" + switchTo.getTypeName())); 433 434 mContext.sendStickyBroadcast(intent); 435 /* 436 * If the failover network is already connected, then immediately send out 437 * a followup broadcast indicating successful failover 438 */ 439 if (switchTo != null && otherNetworkConnected) 440 sendConnectedBroadcast(switchTo); 441 } 442 443 private void sendConnectedBroadcast(NetworkInfo info) { 444 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); 445 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); 446 if (info.isFailover()) { 447 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); 448 info.setFailover(false); 449 } 450 if (info.getReason() != null) { 451 intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason()); 452 } 453 if (info.getExtraInfo() != null) { 454 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo()); 455 } 456 mContext.sendStickyBroadcast(intent); 457 } 458 459 /** 460 * Called when an attempt to fail over to another network has failed. 461 * @param info the {@link NetworkInfo} for the failed network 462 */ 463 private void handleConnectionFailure(NetworkInfo info) { 464 mNetTrackers[info.getType()].setTeardownRequested(false); 465 if (getActiveNetworkInfo() == null) { 466 String reason = info.getReason(); 467 String extraInfo = info.getExtraInfo(); 468 469 if (DBG) { 470 String reasonText; 471 if (reason == null) { 472 reasonText = "."; 473 } else { 474 reasonText = " (" + reason + ")."; 475 } 476 Log.v(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText); 477 } 478 479 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); 480 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); 481 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); 482 if (reason != null) { 483 intent.putExtra(ConnectivityManager.EXTRA_REASON, reason); 484 } 485 if (extraInfo != null) { 486 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo); 487 } 488 if (info.isFailover()) { 489 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); 490 info.setFailover(false); 491 } 492 mContext.sendStickyBroadcast(intent); 493 } 494 } 495 496 private void handleConnect(NetworkInfo info) { 497 if (DBG) Log.v(TAG, "Handle CONNECT for " + info.getTypeName()); 498 499 // snapshot isFailover, because sendConnectedBroadcast() resets it 500 boolean isFailover = info.isFailover(); 501 NetworkStateTracker thisNet = mNetTrackers[info.getType()]; 502 NetworkStateTracker deadnet = null; 503 NetworkStateTracker otherNet; 504 if (info.getType() == ConnectivityManager.TYPE_MOBILE) { 505 otherNet = mWifiStateTracker; 506 } else /* info().getType() == TYPE_WIFI */ { 507 otherNet = mMobileDataStateTracker; 508 } 509 /* 510 * Check policy to see whether we are connected to a non-preferred 511 * network that now needs to be torn down. 512 */ 513 NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo(); 514 NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo(); 515 if (wifiInfo.isConnected() && mobileInfo.isConnected()) { 516 if (mNetworkPreference == ConnectivityManager.TYPE_WIFI) 517 deadnet = mMobileDataStateTracker; 518 else 519 deadnet = mWifiStateTracker; 520 } 521 522 boolean toredown = false; 523 thisNet.setTeardownRequested(false); 524 if (!mTestMode && deadnet != null) { 525 if (DBG) Log.v(TAG, "Policy requires " + 526 deadnet.getNetworkInfo().getTypeName() + " teardown"); 527 toredown = teardown(deadnet); 528 if (DBG && !toredown) { 529 Log.d(TAG, "Network declined teardown request"); 530 } 531 } 532 533 /* 534 * Note that if toredown is true, deadnet cannot be null, so there is 535 * no danger of a null pointer exception here.. 536 */ 537 if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) { 538 mActiveNetwork = thisNet; 539 if (DBG) Log.v(TAG, "Sending CONNECT bcast for " + info.getTypeName()); 540 thisNet.updateNetworkSettings(); 541 sendConnectedBroadcast(info); 542 if (isFailover) { 543 otherNet.releaseWakeLock(); 544 } 545 } else { 546 if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn down network " + 547 info.getTypeName()); 548 } 549 } 550 551 private void handleScanResultsAvailable(NetworkInfo info) { 552 int networkType = info.getType(); 553 if (networkType != ConnectivityManager.TYPE_WIFI) { 554 if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network." 555 + " Don't know how to handle."); 556 } 557 558 mNetTrackers[networkType].interpretScanResultsAvailable(); 559 } 560 561 private void handleNotificationChange(boolean visible, int id, Notification notification) { 562 NotificationManager notificationManager = (NotificationManager) mContext 563 .getSystemService(Context.NOTIFICATION_SERVICE); 564 565 if (visible) { 566 notificationManager.notify(id, notification); 567 } else { 568 notificationManager.cancel(id); 569 } 570 } 571 572 /** 573 * After any kind of change in the connectivity state of any network, 574 * make sure that anything that depends on the connectivity state of 575 * more than one network is set up correctly. We're mainly concerned 576 * with making sure that the list of DNS servers is set up according 577 * to which networks are connected, and ensuring that the right routing 578 * table entries exist. 579 */ 580 private void handleConnectivityChange() { 581 /* 582 * If both mobile and wifi are enabled, add the host routes that 583 * will allow MMS traffic to pass on the mobile network. But 584 * remove the default route for the mobile network, so that there 585 * will be only one default route, to ensure that all traffic 586 * except MMS will travel via Wi-Fi. 587 */ 588 int numConnectedNets = handleConfigurationChange(); 589 if (numConnectedNets > 1) { 590 mMobileDataStateTracker.addPrivateRoutes(); 591 mMobileDataStateTracker.removeDefaultRoute(); 592 } else if (mMobileDataStateTracker.getNetworkInfo().isConnected()) { 593 mMobileDataStateTracker.removePrivateRoutes(); 594 mMobileDataStateTracker.restoreDefaultRoute(); 595 } 596 } 597 598 private int handleConfigurationChange() { 599 /* 600 * Set DNS properties. Always put Wi-Fi entries at the front of 601 * the list if it is active. 602 */ 603 int index = 1; 604 String lastDns = ""; 605 int numConnectedNets = 0; 606 int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI; 607 int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue; 608 609 for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue; netType += incrValue) { 610 NetworkStateTracker nt = mNetTrackers[netType]; 611 if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) { 612 ++numConnectedNets; 613 String[] dnsList = nt.getNameServers(); 614 for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) { 615 // skip duplicate entries 616 if (!dnsList[i].equals(lastDns)) { 617 SystemProperties.set("net.dns" + index++, dnsList[i]); 618 lastDns = dnsList[i]; 619 } 620 } 621 } 622 } 623 // Null out any DNS properties that are no longer used 624 for (int i = index; i <= mNumDnsEntries; i++) { 625 SystemProperties.set("net.dns" + i, ""); 626 } 627 mNumDnsEntries = index - 1; 628 // Notify the name resolver library of the change 629 SystemProperties.set("net.dnschange", String.valueOf(sDnsChangeCounter++)); 630 return numConnectedNets; 631 } 632 633 @Override 634 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 635 if (mContext.checkCallingPermission(android.Manifest.permission.DUMP) 636 != PackageManager.PERMISSION_GRANTED) { 637 pw.println("Permission Denial: can't dump ConnectivityService from from pid=" 638 + Binder.getCallingPid() 639 + ", uid=" + Binder.getCallingUid()); 640 return; 641 } 642 if (mActiveNetwork == null) { 643 pw.println("No active network"); 644 } else { 645 pw.println("Active network: " + mActiveNetwork.getNetworkInfo().getTypeName()); 646 } 647 pw.println(); 648 for (NetworkStateTracker nst : mNetTrackers) { 649 pw.println(nst.getNetworkInfo()); 650 pw.println(nst); 651 pw.println(); 652 } 653 } 654 655 private class MyHandler extends Handler { 656 @Override 657 public void handleMessage(Message msg) { 658 NetworkInfo info; 659 switch (msg.what) { 660 case NetworkStateTracker.EVENT_STATE_CHANGED: 661 info = (NetworkInfo) msg.obj; 662 if (DBG) Log.v(TAG, "ConnectivityChange for " + info.getTypeName() + ": " + 663 info.getState() + "/" + info.getDetailedState()); 664 665 // Connectivity state changed: 666 // [31-13] Reserved for future use 667 // [12-9] Network subtype (for mobile network, as defined by TelephonyManager) 668 // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState) 669 // [2-0] Network type (as defined by ConnectivityManager) 670 int eventLogParam = (info.getType() & 0x7) | 671 ((info.getDetailedState().ordinal() & 0x3f) << 3) | 672 (info.getSubtype() << 9); 673 EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam); 674 675 if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) { 676 handleConnectionFailure(info); 677 } else if (info.getState() == NetworkInfo.State.DISCONNECTED) { 678 handleDisconnect(info); 679 } else if (info.getState() == NetworkInfo.State.SUSPENDED) { 680 // TODO: need to think this over. 681 // the logic here is, handle SUSPENDED the same as DISCONNECTED. The 682 // only difference being we are broadcasting an intent with NetworkInfo 683 // that's suspended. This allows the applications an opportunity to 684 // handle DISCONNECTED and SUSPENDED differently, or not. 685 handleDisconnect(info); 686 } else if (info.getState() == NetworkInfo.State.CONNECTED) { 687 handleConnect(info); 688 } 689 handleConnectivityChange(); 690 break; 691 692 case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE: 693 info = (NetworkInfo) msg.obj; 694 handleScanResultsAvailable(info); 695 break; 696 697 case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED: 698 handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj); 699 700 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: 701 handleConfigurationChange(); 702 break; 703 704 case NetworkStateTracker.EVENT_ROAMING_CHANGED: 705 // fill me in 706 break; 707 708 case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: 709 // fill me in 710 break; 711 } 712 } 713 } 714} 715