ConnectivityService.java revision 09ac3c3cbc6278af127cffedb9d534449e63b683
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.IBinder; 34import android.os.Looper; 35import android.os.Message; 36import android.os.RemoteException; 37import android.os.ServiceManager; 38import android.os.SystemProperties; 39import android.provider.Settings; 40import android.text.TextUtils; 41import android.util.EventLog; 42import android.util.Log; 43 44import com.android.internal.telephony.Phone; 45 46import java.io.FileDescriptor; 47import java.io.PrintWriter; 48import java.util.ArrayList; 49import java.util.List; 50 51/** 52 * @hide 53 */ 54public class ConnectivityService extends IConnectivityManager.Stub { 55 56 private static final boolean DBG = true; 57 private static final String TAG = "ConnectivityService"; 58 59 // how long to wait before switching back to a radio's default network 60 private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000; 61 // system property that can override the above value 62 private static final String NETWORK_RESTORE_DELAY_PROP_NAME = 63 "android.telephony.apn-restore"; 64 65 /** 66 * Sometimes we want to refer to the individual network state 67 * trackers separately, and sometimes we just want to treat them 68 * abstractly. 69 */ 70 private NetworkStateTracker mNetTrackers[]; 71 72 /** 73 * A per Net list of the PID's that requested access to the net 74 * used both as a refcount and for per-PID DNS selection 75 */ 76 private List mNetRequestersPids[]; 77 78 private WifiWatchdogService mWifiWatchdogService; 79 80 // priority order of the nettrackers 81 // (excluding dynamically set mNetworkPreference) 82 // TODO - move mNetworkTypePreference into this 83 private int[] mPriorityList; 84 85 private Context mContext; 86 private int mNetworkPreference; 87 private int mActiveDefaultNetwork = -1; 88 89 private int mNumDnsEntries; 90 91 private boolean mTestMode; 92 private static ConnectivityService sServiceInstance; 93 94 private Handler mHandler; 95 96 // list of DeathRecipients used to make sure features are turned off when 97 // a process dies 98 private List mFeatureUsers; 99 100 private boolean mSystemReady; 101 private Intent mInitialBroadcast; 102 103 private static class NetworkAttributes { 104 /** 105 * Class for holding settings read from resources. 106 */ 107 public String mName; 108 public int mType; 109 public int mRadio; 110 public int mPriority; 111 public NetworkInfo.State mLastState; 112 public NetworkAttributes(String init) { 113 String fragments[] = init.split(","); 114 mName = fragments[0].toLowerCase(); 115 mType = Integer.parseInt(fragments[1]); 116 mRadio = Integer.parseInt(fragments[2]); 117 mPriority = Integer.parseInt(fragments[3]); 118 mLastState = NetworkInfo.State.UNKNOWN; 119 } 120 public boolean isDefault() { 121 return (mType == mRadio); 122 } 123 } 124 NetworkAttributes[] mNetAttributes; 125 int mNetworksDefined; 126 127 private static class RadioAttributes { 128 public int mSimultaneity; 129 public int mType; 130 public RadioAttributes(String init) { 131 String fragments[] = init.split(","); 132 mType = Integer.parseInt(fragments[0]); 133 mSimultaneity = Integer.parseInt(fragments[1]); 134 } 135 } 136 RadioAttributes[] mRadioAttributes; 137 138 private static class ConnectivityThread extends Thread { 139 private Context mContext; 140 141 private ConnectivityThread(Context context) { 142 super("ConnectivityThread"); 143 mContext = context; 144 } 145 146 @Override 147 public void run() { 148 Looper.prepare(); 149 synchronized (this) { 150 sServiceInstance = new ConnectivityService(mContext); 151 notifyAll(); 152 } 153 Looper.loop(); 154 } 155 156 public static ConnectivityService getServiceInstance(Context context) { 157 ConnectivityThread thread = new ConnectivityThread(context); 158 thread.start(); 159 160 synchronized (thread) { 161 while (sServiceInstance == null) { 162 try { 163 // Wait until sServiceInstance has been initialized. 164 thread.wait(); 165 } catch (InterruptedException ignore) { 166 Log.e(TAG, 167 "Unexpected InterruptedException while waiting"+ 168 " for ConnectivityService thread"); 169 } 170 } 171 } 172 173 return sServiceInstance; 174 } 175 } 176 177 public static ConnectivityService getInstance(Context context) { 178 return ConnectivityThread.getServiceInstance(context); 179 } 180 181 private ConnectivityService(Context context) { 182 if (DBG) Log.v(TAG, "ConnectivityService starting up"); 183 184 // setup our unique device name 185 String id = Settings.Secure.getString(context.getContentResolver(), 186 Settings.Secure.ANDROID_ID); 187 if (id != null && id.length() > 0) { 188 String name = new String("android_").concat(id); 189 SystemProperties.set("net.hostname", name); 190 } 191 192 mContext = context; 193 mNetTrackers = new NetworkStateTracker[ 194 ConnectivityManager.MAX_NETWORK_TYPE+1]; 195 mHandler = new MyHandler(); 196 197 mNetworkPreference = getPersistedNetworkPreference(); 198 199 mRadioAttributes = new RadioAttributes[ConnectivityManager.MAX_RADIO_TYPE+1]; 200 mNetAttributes = new NetworkAttributes[ConnectivityManager.MAX_NETWORK_TYPE+1]; 201 202 // Load device network attributes from resources 203 String[] raStrings = context.getResources().getStringArray( 204 com.android.internal.R.array.radioAttributes); 205 for (String raString : raStrings) { 206 RadioAttributes r = new RadioAttributes(raString); 207 if (r.mType > ConnectivityManager.MAX_RADIO_TYPE) { 208 Log.e(TAG, "Error in radioAttributes - ignoring attempt to define type " + r.mType); 209 continue; 210 } 211 if (mRadioAttributes[r.mType] != null) { 212 Log.e(TAG, "Error in radioAttributes - ignoring attempt to redefine type " + 213 r.mType); 214 continue; 215 } 216 mRadioAttributes[r.mType] = r; 217 } 218 219 String[] naStrings = context.getResources().getStringArray( 220 com.android.internal.R.array.networkAttributes); 221 for (String naString : naStrings) { 222 try { 223 NetworkAttributes n = new NetworkAttributes(naString); 224 if (n.mType > ConnectivityManager.MAX_NETWORK_TYPE) { 225 Log.e(TAG, "Error in networkAttributes - ignoring attempt to define type " + 226 n.mType); 227 continue; 228 } 229 if (mNetAttributes[n.mType] != null) { 230 Log.e(TAG, "Error in networkAttributes - ignoring attempt to redefine type " + 231 n.mType); 232 continue; 233 } 234 if (mRadioAttributes[n.mRadio] == null) { 235 Log.e(TAG, "Error in networkAttributes - ignoring attempt to use undefined " + 236 "radio " + n.mRadio + " in network type " + n.mType); 237 continue; 238 } 239 mNetAttributes[n.mType] = n; 240 mNetworksDefined++; 241 } catch(Exception e) { 242 // ignore it - leave the entry null 243 } 244 } 245 246 // high priority first 247 mPriorityList = new int[mNetworksDefined]; 248 { 249 int insertionPoint = mNetworksDefined-1; 250 int currentLowest = 0; 251 int nextLowest = 0; 252 while (insertionPoint > -1) { 253 for (NetworkAttributes na : mNetAttributes) { 254 if (na == null) continue; 255 if (na.mPriority < currentLowest) continue; 256 if (na.mPriority > currentLowest) { 257 if (na.mPriority < nextLowest || nextLowest == 0) { 258 nextLowest = na.mPriority; 259 } 260 continue; 261 } 262 mPriorityList[insertionPoint--] = na.mType; 263 } 264 currentLowest = nextLowest; 265 nextLowest = 0; 266 } 267 } 268 269 mNetRequestersPids = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1]; 270 for (int i : mPriorityList) { 271 mNetRequestersPids[i] = new ArrayList(); 272 } 273 274 mFeatureUsers = new ArrayList(); 275 276 mNumDnsEntries = 0; 277 278 mTestMode = SystemProperties.get("cm.test.mode").equals("true") 279 && SystemProperties.get("ro.build.type").equals("eng"); 280 /* 281 * Create the network state trackers for Wi-Fi and mobile 282 * data. Maybe this could be done with a factory class, 283 * but it's not clear that it's worth it, given that 284 * the number of different network types is not going 285 * to change very often. 286 */ 287 for (int netType : mPriorityList) { 288 switch (mNetAttributes[netType].mRadio) { 289 case ConnectivityManager.TYPE_WIFI: 290 if (DBG) Log.v(TAG, "Starting Wifi Service."); 291 WifiStateTracker wst = new WifiStateTracker(context, mHandler); 292 WifiService wifiService = new WifiService(context, wst); 293 ServiceManager.addService(Context.WIFI_SERVICE, wifiService); 294 mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst; 295 wst.startMonitoring(); 296 297 // Constructing this starts it too 298 mWifiWatchdogService = new WifiWatchdogService(context, wst); 299 break; 300 case ConnectivityManager.TYPE_MOBILE: 301 mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler, 302 netType, mNetAttributes[netType].mName); 303 mNetTrackers[netType].startMonitoring(); 304 break; 305 default: 306 Log.e(TAG, "Trying to create a DataStateTracker for an unknown radio type " + 307 mNetAttributes[netType].mRadio); 308 continue; 309 } 310 } 311 } 312 313 314 /** 315 * Sets the preferred network. 316 * @param preference the new preference 317 */ 318 public synchronized void setNetworkPreference(int preference) { 319 enforceChangePermission(); 320 if (ConnectivityManager.isNetworkTypeValid(preference) && 321 mNetAttributes[preference] != null && 322 mNetAttributes[preference].isDefault()) { 323 if (mNetworkPreference != preference) { 324 persistNetworkPreference(preference); 325 mNetworkPreference = preference; 326 enforcePreference(); 327 } 328 } 329 } 330 331 public int getNetworkPreference() { 332 enforceAccessPermission(); 333 return mNetworkPreference; 334 } 335 336 private void persistNetworkPreference(int networkPreference) { 337 final ContentResolver cr = mContext.getContentResolver(); 338 Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, 339 networkPreference); 340 } 341 342 private int getPersistedNetworkPreference() { 343 final ContentResolver cr = mContext.getContentResolver(); 344 345 final int networkPrefSetting = Settings.Secure 346 .getInt(cr, Settings.Secure.NETWORK_PREFERENCE, -1); 347 if (networkPrefSetting != -1) { 348 return networkPrefSetting; 349 } 350 351 return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE; 352 } 353 354 /** 355 * Make the state of network connectivity conform to the preference settings 356 * In this method, we only tear down a non-preferred network. Establishing 357 * a connection to the preferred network is taken care of when we handle 358 * the disconnect event from the non-preferred network 359 * (see {@link #handleDisconnect(NetworkInfo)}). 360 */ 361 private void enforcePreference() { 362 if (mNetTrackers[mNetworkPreference].getNetworkInfo().isConnected()) 363 return; 364 365 if (!mNetTrackers[mNetworkPreference].isAvailable()) 366 return; 367 368 for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) { 369 if (t != mNetworkPreference && mNetTrackers[t] != null && 370 mNetTrackers[t].getNetworkInfo().isConnected()) { 371 if (DBG) { 372 Log.d(TAG, "tearing down " + 373 mNetTrackers[t].getNetworkInfo() + 374 " in enforcePreference"); 375 } 376 teardown(mNetTrackers[t]); 377 } 378 } 379 } 380 381 private boolean teardown(NetworkStateTracker netTracker) { 382 if (netTracker.teardown()) { 383 netTracker.setTeardownRequested(true); 384 return true; 385 } else { 386 return false; 387 } 388 } 389 390 /** 391 * Return NetworkInfo for the active (i.e., connected) network interface. 392 * It is assumed that at most one network is active at a time. If more 393 * than one is active, it is indeterminate which will be returned. 394 * @return the info for the active network, or {@code null} if none is 395 * active 396 */ 397 public NetworkInfo getActiveNetworkInfo() { 398 enforceAccessPermission(); 399 for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) { 400 if (mNetAttributes[type] == null || !mNetAttributes[type].isDefault()) { 401 continue; 402 } 403 NetworkStateTracker t = mNetTrackers[type]; 404 NetworkInfo info = t.getNetworkInfo(); 405 if (info.isConnected()) { 406 if (DBG && type != mActiveDefaultNetwork) Log.e(TAG, 407 "connected default network is not " + 408 "mActiveDefaultNetwork!"); 409 return info; 410 } 411 } 412 return null; 413 } 414 415 public NetworkInfo getNetworkInfo(int networkType) { 416 enforceAccessPermission(); 417 if (ConnectivityManager.isNetworkTypeValid(networkType)) { 418 NetworkStateTracker t = mNetTrackers[networkType]; 419 if (t != null) 420 return t.getNetworkInfo(); 421 } 422 return null; 423 } 424 425 public NetworkInfo[] getAllNetworkInfo() { 426 enforceAccessPermission(); 427 NetworkInfo[] result = new NetworkInfo[mNetworksDefined]; 428 int i = 0; 429 for (NetworkStateTracker t : mNetTrackers) { 430 if(t != null) result[i++] = t.getNetworkInfo(); 431 } 432 return result; 433 } 434 435 public boolean setRadios(boolean turnOn) { 436 boolean result = true; 437 enforceChangePermission(); 438 for (NetworkStateTracker t : mNetTrackers) { 439 if (t != null) result = t.setRadio(turnOn) && result; 440 } 441 return result; 442 } 443 444 public boolean setRadio(int netType, boolean turnOn) { 445 enforceChangePermission(); 446 if (!ConnectivityManager.isNetworkTypeValid(netType)) { 447 return false; 448 } 449 NetworkStateTracker tracker = mNetTrackers[netType]; 450 return tracker != null && tracker.setRadio(turnOn); 451 } 452 453 /** 454 * Used to notice when the calling process dies so we can self-expire 455 * 456 * Also used to know if the process has cleaned up after itself when 457 * our auto-expire timer goes off. The timer has a link to an object. 458 * 459 */ 460 private class FeatureUser implements IBinder.DeathRecipient { 461 int mNetworkType; 462 String mFeature; 463 IBinder mBinder; 464 int mPid; 465 int mUid; 466 long mCreateTime; 467 468 FeatureUser(int type, String feature, IBinder binder) { 469 super(); 470 mNetworkType = type; 471 mFeature = feature; 472 mBinder = binder; 473 mPid = getCallingPid(); 474 mUid = getCallingUid(); 475 mCreateTime = System.currentTimeMillis(); 476 477 try { 478 mBinder.linkToDeath(this, 0); 479 } catch (RemoteException e) { 480 binderDied(); 481 } 482 } 483 484 void unlinkDeathRecipient() { 485 mBinder.unlinkToDeath(this, 0); 486 } 487 488 public void binderDied() { 489 Log.d(TAG, "ConnectivityService FeatureUser binderDied(" + 490 mNetworkType + ", " + mFeature + ", " + mBinder + "), created " + 491 (System.currentTimeMillis() - mCreateTime) + " mSec ago"); 492 stopUsingNetworkFeature(this, false); 493 } 494 495 public void expire() { 496 Log.d(TAG, "ConnectivityService FeatureUser expire(" + 497 mNetworkType + ", " + mFeature + ", " + mBinder +"), created " + 498 (System.currentTimeMillis() - mCreateTime) + " mSec ago"); 499 stopUsingNetworkFeature(this, false); 500 } 501 502 public String toString() { 503 return "FeatureUser("+mNetworkType+","+mFeature+","+mPid+","+mUid+"), created " + 504 (System.currentTimeMillis() - mCreateTime) + " mSec ago"; 505 } 506 } 507 508 // javadoc from interface 509 public int startUsingNetworkFeature(int networkType, String feature, 510 IBinder binder) { 511 if (DBG) { 512 Log.d(TAG, "startUsingNetworkFeature for net " + networkType + 513 ": " + feature); 514 } 515 enforceChangePermission(); 516 if (!ConnectivityManager.isNetworkTypeValid(networkType) || 517 mNetAttributes[networkType] == null) { 518 return Phone.APN_REQUEST_FAILED; 519 } 520 521 FeatureUser f = new FeatureUser(networkType, feature, binder); 522 523 // TODO - move this into the MobileDataStateTracker 524 int usedNetworkType = networkType; 525 if(networkType == ConnectivityManager.TYPE_MOBILE) { 526 if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { 527 usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS; 528 } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { 529 usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL; 530 } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) { 531 usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN; 532 } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) { 533 usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI; 534 } 535 } 536 NetworkStateTracker network = mNetTrackers[usedNetworkType]; 537 if (network != null) { 538 if (usedNetworkType != networkType) { 539 Integer currentPid = new Integer(getCallingPid()); 540 541 NetworkStateTracker radio = mNetTrackers[networkType]; 542 NetworkInfo ni = network.getNetworkInfo(); 543 544 if (ni.isAvailable() == false) { 545 if (DBG) Log.d(TAG, "special network not available"); 546 return Phone.APN_TYPE_NOT_AVAILABLE; 547 } 548 549 synchronized(this) { 550 mFeatureUsers.add(f); 551 if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) { 552 // this gets used for per-pid dns when connected 553 mNetRequestersPids[usedNetworkType].add(currentPid); 554 } 555 } 556 mHandler.sendMessageDelayed(mHandler.obtainMessage( 557 NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK, 558 f), getRestoreDefaultNetworkDelay()); 559 560 561 if ((ni.isConnectedOrConnecting() == true) && 562 !network.isTeardownRequested()) { 563 if (ni.isConnected() == true) { 564 // add the pid-specific dns 565 handleDnsConfigurationChange(); 566 if (DBG) Log.d(TAG, "special network already active"); 567 return Phone.APN_ALREADY_ACTIVE; 568 } 569 if (DBG) Log.d(TAG, "special network already connecting"); 570 return Phone.APN_REQUEST_STARTED; 571 } 572 573 // check if the radio in play can make another contact 574 // assume if cannot for now 575 576 if (DBG) Log.d(TAG, "reconnecting to special network"); 577 network.reconnect(); 578 return Phone.APN_REQUEST_STARTED; 579 } else { 580 synchronized(this) { 581 mFeatureUsers.add(f); 582 } 583 mHandler.sendMessageDelayed(mHandler.obtainMessage( 584 NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK, 585 f), getRestoreDefaultNetworkDelay()); 586 587 return network.startUsingNetworkFeature(feature, 588 getCallingPid(), getCallingUid()); 589 } 590 } 591 return Phone.APN_TYPE_NOT_AVAILABLE; 592 } 593 594 // javadoc from interface 595 public int stopUsingNetworkFeature(int networkType, String feature) { 596 enforceChangePermission(); 597 598 int pid = getCallingPid(); 599 int uid = getCallingUid(); 600 601 FeatureUser u = null; 602 boolean found = false; 603 604 synchronized(this) { 605 for (int i = 0; i < mFeatureUsers.size() ; i++) { 606 u = (FeatureUser)mFeatureUsers.get(i); 607 if (uid == u.mUid && pid == u.mPid && 608 networkType == u.mNetworkType && 609 TextUtils.equals(feature, u.mFeature)) { 610 found = true; 611 break; 612 } 613 } 614 } 615 if (found && u != null) { 616 // stop regardless of how many other time this proc had called start 617 return stopUsingNetworkFeature(u, true); 618 } else { 619 // none found! 620 if (DBG) Log.d(TAG, "ignoring stopUsingNetworkFeature - not a live request"); 621 return 1; 622 } 623 } 624 625 private int stopUsingNetworkFeature(FeatureUser u, boolean ignoreDups) { 626 int networkType = u.mNetworkType; 627 String feature = u.mFeature; 628 int pid = u.mPid; 629 int uid = u.mUid; 630 631 NetworkStateTracker tracker = null; 632 boolean callTeardown = false; // used to carry our decision outside of sync block 633 634 if (DBG) { 635 Log.d(TAG, "stopUsingNetworkFeature for net " + networkType + 636 ": " + feature); 637 } 638 639 if (!ConnectivityManager.isNetworkTypeValid(networkType)) { 640 return -1; 641 } 642 643 // need to link the mFeatureUsers list with the mNetRequestersPids state in this 644 // sync block 645 synchronized(this) { 646 // check if this process still has an outstanding start request 647 if (!mFeatureUsers.contains(u)) { 648 return 1; 649 } 650 u.unlinkDeathRecipient(); 651 mFeatureUsers.remove(mFeatureUsers.indexOf(u)); 652 // If we care about duplicate requests, check for that here. 653 // 654 // This is done to support the extension of a request - the app 655 // can request we start the network feature again and renew the 656 // auto-shutoff delay. Normal "stop" calls from the app though 657 // do not pay attention to duplicate requests - in effect the 658 // API does not refcount and a single stop will counter multiple starts. 659 if (ignoreDups == false) { 660 for (int i = 0; i < mFeatureUsers.size() ; i++) { 661 FeatureUser x = (FeatureUser)mFeatureUsers.get(i); 662 if (x.mUid == u.mUid && x.mPid == u.mPid && 663 x.mNetworkType == u.mNetworkType && 664 TextUtils.equals(x.mFeature, u.mFeature)) { 665 if (DBG) Log.d(TAG, "ignoring stopUsingNetworkFeature as dup is found"); 666 return 1; 667 } 668 } 669 } 670 671 // TODO - move to MobileDataStateTracker 672 int usedNetworkType = networkType; 673 if (networkType == ConnectivityManager.TYPE_MOBILE) { 674 if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { 675 usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS; 676 } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { 677 usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL; 678 } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) { 679 usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN; 680 } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) { 681 usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI; 682 } 683 } 684 tracker = mNetTrackers[usedNetworkType]; 685 if (tracker == null) { 686 return -1; 687 } 688 if (usedNetworkType != networkType) { 689 Integer currentPid = new Integer(pid); 690 mNetRequestersPids[usedNetworkType].remove(currentPid); 691 reassessPidDns(pid, true); 692 if (mNetRequestersPids[usedNetworkType].size() != 0) { 693 if (DBG) Log.d(TAG, "not tearing down special network - " + 694 "others still using it"); 695 return 1; 696 } 697 callTeardown = true; 698 } 699 } 700 701 if (callTeardown) { 702 tracker.teardown(); 703 return 1; 704 } else { 705 // do it the old fashioned way 706 return tracker.stopUsingNetworkFeature(feature, pid, uid); 707 } 708 } 709 710 /** 711 * Ensure that a network route exists to deliver traffic to the specified 712 * host via the specified network interface. 713 * @param networkType the type of the network over which traffic to the 714 * specified host is to be routed 715 * @param hostAddress the IP address of the host to which the route is 716 * desired 717 * @return {@code true} on success, {@code false} on failure 718 */ 719 public boolean requestRouteToHost(int networkType, int hostAddress) { 720 enforceChangePermission(); 721 if (!ConnectivityManager.isNetworkTypeValid(networkType)) { 722 return false; 723 } 724 NetworkStateTracker tracker = mNetTrackers[networkType]; 725 726 if (tracker == null || !tracker.getNetworkInfo().isConnected() || 727 tracker.isTeardownRequested()) { 728 if (DBG) { 729 Log.d(TAG, "requestRouteToHost on down network (" + networkType + ") - dropped"); 730 } 731 return false; 732 } 733 return tracker.requestRouteToHost(hostAddress); 734 } 735 736 /** 737 * @see ConnectivityManager#getBackgroundDataSetting() 738 */ 739 public boolean getBackgroundDataSetting() { 740 return Settings.Secure.getInt(mContext.getContentResolver(), 741 Settings.Secure.BACKGROUND_DATA, 1) == 1; 742 } 743 744 /** 745 * @see ConnectivityManager#setBackgroundDataSetting(boolean) 746 */ 747 public void setBackgroundDataSetting(boolean allowBackgroundDataUsage) { 748 mContext.enforceCallingOrSelfPermission( 749 android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING, 750 "ConnectivityService"); 751 752 if (getBackgroundDataSetting() == allowBackgroundDataUsage) return; 753 754 Settings.Secure.putInt(mContext.getContentResolver(), 755 Settings.Secure.BACKGROUND_DATA, 756 allowBackgroundDataUsage ? 1 : 0); 757 758 Intent broadcast = new Intent( 759 ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); 760 mContext.sendBroadcast(broadcast); 761 } 762 763 private int getNumConnectedNetworks() { 764 int numConnectedNets = 0; 765 766 for (NetworkStateTracker nt : mNetTrackers) { 767 if (nt != null && nt.getNetworkInfo().isConnected() && 768 !nt.isTeardownRequested()) { 769 ++numConnectedNets; 770 } 771 } 772 return numConnectedNets; 773 } 774 775 private void enforceAccessPermission() { 776 mContext.enforceCallingOrSelfPermission( 777 android.Manifest.permission.ACCESS_NETWORK_STATE, 778 "ConnectivityService"); 779 } 780 781 private void enforceChangePermission() { 782 mContext.enforceCallingOrSelfPermission( 783 android.Manifest.permission.CHANGE_NETWORK_STATE, 784 "ConnectivityService"); 785 } 786 787 /** 788 * Handle a {@code DISCONNECTED} event. If this pertains to the non-active 789 * network, we ignore it. If it is for the active network, we send out a 790 * broadcast. But first, we check whether it might be possible to connect 791 * to a different network. 792 * @param info the {@code NetworkInfo} for the network 793 */ 794 private void handleDisconnect(NetworkInfo info) { 795 796 int prevNetType = info.getType(); 797 798 mNetTrackers[prevNetType].setTeardownRequested(false); 799 /* 800 * If the disconnected network is not the active one, then don't report 801 * this as a loss of connectivity. What probably happened is that we're 802 * getting the disconnect for a network that we explicitly disabled 803 * in accordance with network preference policies. 804 */ 805 if (!mNetAttributes[prevNetType].isDefault()) { 806 List pids = mNetRequestersPids[prevNetType]; 807 for (int i = 0; i<pids.size(); i++) { 808 Integer pid = (Integer)pids.get(i); 809 // will remove them because the net's no longer connected 810 // need to do this now as only now do we know the pids and 811 // can properly null things that are no longer referenced. 812 reassessPidDns(pid.intValue(), false); 813 } 814 } 815 816 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); 817 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 818 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); 819 if (info.isFailover()) { 820 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); 821 info.setFailover(false); 822 } 823 if (info.getReason() != null) { 824 intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason()); 825 } 826 if (info.getExtraInfo() != null) { 827 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, 828 info.getExtraInfo()); 829 } 830 831 NetworkStateTracker newNet = tryFailover(prevNetType); 832 if (newNet != null) { 833 NetworkInfo switchTo = newNet.getNetworkInfo(); 834 intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo); 835 } else { 836 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); 837 } 838 // do this before we broadcast the change 839 handleConnectivityChange(); 840 841 sendStickyBroadcast(intent); 842 /* 843 * If the failover network is already connected, then immediately send 844 * out a followup broadcast indicating successful failover 845 */ 846 if (newNet != null && newNet.getNetworkInfo().isConnected()) { 847 sendConnectedBroadcast(newNet.getNetworkInfo()); 848 } 849 } 850 851 // returns -1 if no failover available 852 private NetworkStateTracker tryFailover(int prevNetType) { 853 /* 854 * If this is a default network, check if other defaults are available 855 * or active 856 */ 857 NetworkStateTracker newNet = null; 858 if (mNetAttributes[prevNetType].isDefault()) { 859 if (mActiveDefaultNetwork == prevNetType) { 860 mActiveDefaultNetwork = -1; 861 } 862 863 int newType = -1; 864 int newPriority = -1; 865 for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) { 866 if (checkType == prevNetType) continue; 867 if (mNetAttributes[checkType] == null) continue; 868 if (mNetAttributes[checkType].isDefault()) { 869 /* TODO - if we have multiple nets we could use 870 * we may want to put more thought into which we choose 871 */ 872 if (checkType == mNetworkPreference) { 873 newType = checkType; 874 break; 875 } 876 if (mNetAttributes[checkType].mPriority > newPriority) { 877 newType = checkType; 878 newPriority = mNetAttributes[newType].mPriority; 879 } 880 } 881 } 882 883 if (newType != -1) { 884 newNet = mNetTrackers[newType]; 885 /** 886 * See if the other network is available to fail over to. 887 * If is not available, we enable it anyway, so that it 888 * will be able to connect when it does become available, 889 * but we report a total loss of connectivity rather than 890 * report that we are attempting to fail over. 891 */ 892 if (newNet.isAvailable()) { 893 NetworkInfo switchTo = newNet.getNetworkInfo(); 894 switchTo.setFailover(true); 895 if (!switchTo.isConnectedOrConnecting() || 896 newNet.isTeardownRequested()) { 897 newNet.reconnect(); 898 } 899 if (DBG) { 900 if (switchTo.isConnected()) { 901 Log.v(TAG, "Switching to already connected " + 902 switchTo.getTypeName()); 903 } else { 904 Log.v(TAG, "Attempting to switch to " + 905 switchTo.getTypeName()); 906 } 907 } 908 } else { 909 newNet.reconnect(); 910 } 911 } 912 } 913 914 return newNet; 915 } 916 917 private void sendConnectedBroadcast(NetworkInfo info) { 918 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); 919 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 920 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); 921 if (info.isFailover()) { 922 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); 923 info.setFailover(false); 924 } 925 if (info.getReason() != null) { 926 intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason()); 927 } 928 if (info.getExtraInfo() != null) { 929 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, 930 info.getExtraInfo()); 931 } 932 sendStickyBroadcast(intent); 933 } 934 935 /** 936 * Called when an attempt to fail over to another network has failed. 937 * @param info the {@link NetworkInfo} for the failed network 938 */ 939 private void handleConnectionFailure(NetworkInfo info) { 940 mNetTrackers[info.getType()].setTeardownRequested(false); 941 942 String reason = info.getReason(); 943 String extraInfo = info.getExtraInfo(); 944 945 if (DBG) { 946 String reasonText; 947 if (reason == null) { 948 reasonText = "."; 949 } else { 950 reasonText = " (" + reason + ")."; 951 } 952 Log.v(TAG, "Attempt to connect to " + info.getTypeName() + 953 " failed" + reasonText); 954 } 955 956 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); 957 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 958 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); 959 if (getActiveNetworkInfo() == null) { 960 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); 961 } 962 if (reason != null) { 963 intent.putExtra(ConnectivityManager.EXTRA_REASON, reason); 964 } 965 if (extraInfo != null) { 966 intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo); 967 } 968 if (info.isFailover()) { 969 intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); 970 info.setFailover(false); 971 } 972 973 NetworkStateTracker newNet = tryFailover(info.getType()); 974 if (newNet != null) { 975 NetworkInfo switchTo = newNet.getNetworkInfo(); 976 intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo); 977 } else { 978 intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); 979 } 980 // do this before we broadcast the change 981 handleConnectivityChange(); 982 983 sendStickyBroadcast(intent); 984 /* 985 * If the failover network is already connected, then immediately send 986 * out a followup broadcast indicating successful failover 987 */ 988 if (newNet != null && newNet.getNetworkInfo().isConnected()) { 989 sendConnectedBroadcast(newNet.getNetworkInfo()); 990 } 991 } 992 993 private void sendStickyBroadcast(Intent intent) { 994 synchronized(this) { 995 if (!mSystemReady) { 996 mInitialBroadcast = new Intent(intent); 997 } 998 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 999 mContext.sendStickyBroadcast(intent); 1000 } 1001 } 1002 1003 void systemReady() { 1004 synchronized(this) { 1005 mSystemReady = true; 1006 if (mInitialBroadcast != null) { 1007 mContext.sendStickyBroadcast(mInitialBroadcast); 1008 mInitialBroadcast = null; 1009 } 1010 } 1011 } 1012 1013 private void handleConnect(NetworkInfo info) { 1014 int type = info.getType(); 1015 1016 // snapshot isFailover, because sendConnectedBroadcast() resets it 1017 boolean isFailover = info.isFailover(); 1018 NetworkStateTracker thisNet = mNetTrackers[type]; 1019 1020 // if this is a default net and other default is running 1021 // kill the one not preferred 1022 if (mNetAttributes[type].isDefault()) { 1023 if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) { 1024 if ((type != mNetworkPreference && 1025 mNetAttributes[mActiveDefaultNetwork].mPriority > 1026 mNetAttributes[type].mPriority) || 1027 mNetworkPreference == mActiveDefaultNetwork) { 1028 // don't accept this one 1029 if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION " + 1030 "to torn down network " + info.getTypeName()); 1031 teardown(thisNet); 1032 return; 1033 } else { 1034 // tear down the other 1035 NetworkStateTracker otherNet = 1036 mNetTrackers[mActiveDefaultNetwork]; 1037 if (DBG) Log.v(TAG, "Policy requires " + 1038 otherNet.getNetworkInfo().getTypeName() + 1039 " teardown"); 1040 if (!teardown(otherNet)) { 1041 Log.e(TAG, "Network declined teardown request"); 1042 return; 1043 } 1044 if (isFailover) { 1045 otherNet.releaseWakeLock(); 1046 } 1047 } 1048 } 1049 mActiveDefaultNetwork = type; 1050 } 1051 thisNet.setTeardownRequested(false); 1052 thisNet.updateNetworkSettings(); 1053 handleConnectivityChange(); 1054 sendConnectedBroadcast(info); 1055 } 1056 1057 private void handleScanResultsAvailable(NetworkInfo info) { 1058 int networkType = info.getType(); 1059 if (networkType != ConnectivityManager.TYPE_WIFI) { 1060 if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + 1061 info.getTypeName() + " network. Don't know how to handle."); 1062 } 1063 1064 mNetTrackers[networkType].interpretScanResultsAvailable(); 1065 } 1066 1067 private void handleNotificationChange(boolean visible, int id, 1068 Notification notification) { 1069 NotificationManager notificationManager = (NotificationManager) mContext 1070 .getSystemService(Context.NOTIFICATION_SERVICE); 1071 1072 if (visible) { 1073 notificationManager.notify(id, notification); 1074 } else { 1075 notificationManager.cancel(id); 1076 } 1077 } 1078 1079 /** 1080 * After any kind of change in the connectivity state of any network, 1081 * make sure that anything that depends on the connectivity state of 1082 * more than one network is set up correctly. We're mainly concerned 1083 * with making sure that the list of DNS servers is set up according 1084 * to which networks are connected, and ensuring that the right routing 1085 * table entries exist. 1086 */ 1087 private void handleConnectivityChange() { 1088 /* 1089 * If a non-default network is enabled, add the host routes that 1090 * will allow it's DNS servers to be accessed. Only 1091 * If both mobile and wifi are enabled, add the host routes that 1092 * will allow MMS traffic to pass on the mobile network. But 1093 * remove the default route for the mobile network, so that there 1094 * will be only one default route, to ensure that all traffic 1095 * except MMS will travel via Wi-Fi. 1096 */ 1097 handleDnsConfigurationChange(); 1098 1099 for (int netType : mPriorityList) { 1100 if (mNetTrackers[netType].getNetworkInfo().isConnected()) { 1101 if (mNetAttributes[netType].isDefault()) { 1102 mNetTrackers[netType].addDefaultRoute(); 1103 } else { 1104 mNetTrackers[netType].addPrivateDnsRoutes(); 1105 } 1106 } else { 1107 if (mNetAttributes[netType].isDefault()) { 1108 mNetTrackers[netType].removeDefaultRoute(); 1109 } else { 1110 mNetTrackers[netType].removePrivateDnsRoutes(); 1111 } 1112 } 1113 } 1114 } 1115 1116 /** 1117 * Adjust the per-process dns entries (net.dns<x>.<pid>) based 1118 * on the highest priority active net which this process requested. 1119 * If there aren't any, clear it out 1120 */ 1121 private void reassessPidDns(int myPid, boolean doBump) 1122 { 1123 if (DBG) Log.d(TAG, "reassessPidDns for pid " + myPid); 1124 for(int i : mPriorityList) { 1125 if (mNetAttributes[i].isDefault()) { 1126 continue; 1127 } 1128 NetworkStateTracker nt = mNetTrackers[i]; 1129 if (nt.getNetworkInfo().isConnected() && 1130 !nt.isTeardownRequested()) { 1131 List pids = mNetRequestersPids[i]; 1132 for (int j=0; j<pids.size(); j++) { 1133 Integer pid = (Integer)pids.get(j); 1134 if (pid.intValue() == myPid) { 1135 String[] dnsList = nt.getNameServers(); 1136 writePidDns(dnsList, myPid); 1137 if (doBump) { 1138 bumpDns(); 1139 } 1140 return; 1141 } 1142 } 1143 } 1144 } 1145 // nothing found - delete 1146 for (int i = 1; ; i++) { 1147 String prop = "net.dns" + i + "." + myPid; 1148 if (SystemProperties.get(prop).length() == 0) { 1149 if (doBump) { 1150 bumpDns(); 1151 } 1152 return; 1153 } 1154 SystemProperties.set(prop, ""); 1155 } 1156 } 1157 1158 private void writePidDns(String[] dnsList, int pid) { 1159 int j = 1; 1160 for (String dns : dnsList) { 1161 if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) { 1162 SystemProperties.set("net.dns" + j++ + "." + pid, dns); 1163 } 1164 } 1165 } 1166 1167 private void bumpDns() { 1168 /* 1169 * Bump the property that tells the name resolver library to reread 1170 * the DNS server list from the properties. 1171 */ 1172 String propVal = SystemProperties.get("net.dnschange"); 1173 int n = 0; 1174 if (propVal.length() != 0) { 1175 try { 1176 n = Integer.parseInt(propVal); 1177 } catch (NumberFormatException e) {} 1178 } 1179 SystemProperties.set("net.dnschange", "" + (n+1)); 1180 } 1181 1182 private void handleDnsConfigurationChange() { 1183 // add default net's dns entries 1184 for (int x = mPriorityList.length-1; x>= 0; x--) { 1185 int netType = mPriorityList[x]; 1186 NetworkStateTracker nt = mNetTrackers[netType]; 1187 if (nt != null && nt.getNetworkInfo().isConnected() && 1188 !nt.isTeardownRequested()) { 1189 String[] dnsList = nt.getNameServers(); 1190 if (mNetAttributes[netType].isDefault()) { 1191 int j = 1; 1192 for (String dns : dnsList) { 1193 if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) { 1194 if (DBG) { 1195 Log.d(TAG, "adding dns " + dns + " for " + 1196 nt.getNetworkInfo().getTypeName()); 1197 } 1198 SystemProperties.set("net.dns" + j++, dns); 1199 } 1200 } 1201 for (int k=j ; k<mNumDnsEntries; k++) { 1202 if (DBG) Log.d(TAG, "erasing net.dns" + k); 1203 SystemProperties.set("net.dns" + k, ""); 1204 } 1205 mNumDnsEntries = j; 1206 } else { 1207 // set per-pid dns for attached secondary nets 1208 List pids = mNetRequestersPids[netType]; 1209 for (int y=0; y< pids.size(); y++) { 1210 Integer pid = (Integer)pids.get(y); 1211 writePidDns(dnsList, pid.intValue()); 1212 } 1213 } 1214 } 1215 } 1216 1217 bumpDns(); 1218 } 1219 1220 private int getRestoreDefaultNetworkDelay() { 1221 String restoreDefaultNetworkDelayStr = SystemProperties.get( 1222 NETWORK_RESTORE_DELAY_PROP_NAME); 1223 if(restoreDefaultNetworkDelayStr != null && 1224 restoreDefaultNetworkDelayStr.length() != 0) { 1225 try { 1226 return Integer.valueOf(restoreDefaultNetworkDelayStr); 1227 } catch (NumberFormatException e) { 1228 } 1229 } 1230 return RESTORE_DEFAULT_NETWORK_DELAY; 1231 } 1232 1233 @Override 1234 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1235 if (mContext.checkCallingOrSelfPermission( 1236 android.Manifest.permission.DUMP) 1237 != PackageManager.PERMISSION_GRANTED) { 1238 pw.println("Permission Denial: can't dump ConnectivityService " + 1239 "from from pid=" + Binder.getCallingPid() + ", uid=" + 1240 Binder.getCallingUid()); 1241 return; 1242 } 1243 pw.println(); 1244 for (NetworkStateTracker nst : mNetTrackers) { 1245 if (nst != null) { 1246 if (nst.getNetworkInfo().isConnected()) { 1247 pw.println("Active network: " + nst.getNetworkInfo(). 1248 getTypeName()); 1249 } 1250 pw.println(nst.getNetworkInfo()); 1251 pw.println(nst); 1252 pw.println(); 1253 } 1254 } 1255 1256 pw.println("Network Requester Pids:"); 1257 for (int net : mPriorityList) { 1258 String pidString = net + ": "; 1259 for (Object pid : mNetRequestersPids[net]) { 1260 pidString = pidString + pid.toString() + ", "; 1261 } 1262 pw.println(pidString); 1263 } 1264 pw.println(); 1265 1266 pw.println("FeatureUsers:"); 1267 for (Object requester : mFeatureUsers) { 1268 pw.println(requester.toString()); 1269 } 1270 pw.println(); 1271 } 1272 1273 // must be stateless - things change under us. 1274 private class MyHandler extends Handler { 1275 @Override 1276 public void handleMessage(Message msg) { 1277 NetworkInfo info; 1278 switch (msg.what) { 1279 case NetworkStateTracker.EVENT_STATE_CHANGED: 1280 info = (NetworkInfo) msg.obj; 1281 int type = info.getType(); 1282 NetworkInfo.State state = info.getState(); 1283 if (mNetAttributes[type].mLastState == state) { 1284 if (DBG) { 1285 // TODO - remove this after we validate the dropping doesn't break anything 1286 Log.d(TAG, "Dropping ConnectivityChange for " + 1287 info.getTypeName() + ": " + 1288 state + "/" + info.getDetailedState()); 1289 } 1290 return; 1291 } 1292 mNetAttributes[type].mLastState = state; 1293 1294 if (DBG) Log.d(TAG, "ConnectivityChange for " + 1295 info.getTypeName() + ": " + 1296 state + "/" + info.getDetailedState()); 1297 1298 // Connectivity state changed: 1299 // [31-13] Reserved for future use 1300 // [12-9] Network subtype (for mobile network, as defined 1301 // by TelephonyManager) 1302 // [8-3] Detailed state ordinal (as defined by 1303 // NetworkInfo.DetailedState) 1304 // [2-0] Network type (as defined by ConnectivityManager) 1305 int eventLogParam = (info.getType() & 0x7) | 1306 ((info.getDetailedState().ordinal() & 0x3f) << 3) | 1307 (info.getSubtype() << 9); 1308 EventLog.writeEvent(EventLogTags.CONNECTIVITY_STATE_CHANGED, 1309 eventLogParam); 1310 1311 if (info.getDetailedState() == 1312 NetworkInfo.DetailedState.FAILED) { 1313 handleConnectionFailure(info); 1314 } else if (state == NetworkInfo.State.DISCONNECTED) { 1315 handleDisconnect(info); 1316 } else if (state == NetworkInfo.State.SUSPENDED) { 1317 // TODO: need to think this over. 1318 // the logic here is, handle SUSPENDED the same as 1319 // DISCONNECTED. The only difference being we are 1320 // broadcasting an intent with NetworkInfo that's 1321 // suspended. This allows the applications an 1322 // opportunity to handle DISCONNECTED and SUSPENDED 1323 // differently, or not. 1324 handleDisconnect(info); 1325 } else if (state == NetworkInfo.State.CONNECTED) { 1326 handleConnect(info); 1327 } 1328 break; 1329 1330 case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE: 1331 info = (NetworkInfo) msg.obj; 1332 handleScanResultsAvailable(info); 1333 break; 1334 1335 case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED: 1336 handleNotificationChange(msg.arg1 == 1, msg.arg2, 1337 (Notification) msg.obj); 1338 1339 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: 1340 handleDnsConfigurationChange(); 1341 break; 1342 1343 case NetworkStateTracker.EVENT_ROAMING_CHANGED: 1344 // fill me in 1345 break; 1346 1347 case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: 1348 // fill me in 1349 break; 1350 case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK: 1351 FeatureUser u = (FeatureUser)msg.obj; 1352 u.expire(); 1353 break; 1354 } 1355 } 1356 } 1357} 1358