WifiStateTracker.java revision baccb91ece4ac650cae465ae36f5d7397a1f32af
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 android.net.wifi; 18 19import android.app.ActivityManagerNative; 20import android.net.NetworkInfo; 21import android.net.NetworkStateTracker; 22import android.net.DhcpInfo; 23import android.net.NetworkUtils; 24import android.net.ConnectivityManager; 25import android.net.NetworkInfo.DetailedState; 26import android.net.NetworkInfo.State; 27import android.os.Message; 28import android.os.Parcelable; 29import android.os.Handler; 30import android.os.HandlerThread; 31import android.os.SystemProperties; 32import android.os.Looper; 33import android.os.RemoteException; 34import android.os.ServiceManager; 35import android.provider.Settings; 36import android.text.TextUtils; 37import android.util.EventLog; 38import android.util.Log; 39import android.util.Config; 40import android.app.Notification; 41import android.app.PendingIntent; 42import android.bluetooth.BluetoothDevice; 43import android.bluetooth.BluetoothHeadset; 44import android.bluetooth.BluetoothA2dp; 45import android.content.ContentResolver; 46import android.content.Intent; 47import android.content.Context; 48import android.database.ContentObserver; 49import com.android.internal.app.IBatteryStats; 50 51import java.util.List; 52import java.util.ArrayList; 53import java.util.Set; 54import java.net.UnknownHostException; 55 56/** 57 * Track the state of Wifi connectivity. All event handling is done here, 58 * and all changes in connectivity state are initiated here. 59 * 60 * @hide 61 */ 62public class WifiStateTracker extends NetworkStateTracker { 63 64 private static final boolean LOCAL_LOGD = Config.LOGD || false; 65 66 private static final String TAG = "WifiStateTracker"; 67 68 // Event log tags (must be in sync with event-log-tags) 69 private static final int EVENTLOG_NETWORK_STATE_CHANGED = 50021; 70 private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50022; 71 private static final int EVENTLOG_DRIVER_STATE_CHANGED = 50023; 72 private static final int EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED = 50024; 73 private static final int EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED = 50025; 74 75 // Event codes 76 private static final int EVENT_SUPPLICANT_CONNECTION = 1; 77 private static final int EVENT_SUPPLICANT_DISCONNECT = 2; 78 private static final int EVENT_SUPPLICANT_STATE_CHANGED = 3; 79 private static final int EVENT_NETWORK_STATE_CHANGED = 4; 80 private static final int EVENT_SCAN_RESULTS_AVAILABLE = 5; 81 private static final int EVENT_INTERFACE_CONFIGURATION_SUCCEEDED = 6; 82 private static final int EVENT_INTERFACE_CONFIGURATION_FAILED = 7; 83 private static final int EVENT_POLL_INTERVAL = 8; 84 private static final int EVENT_DHCP_START = 9; 85 private static final int EVENT_DEFERRED_DISCONNECT = 10; 86 private static final int EVENT_DEFERRED_RECONNECT = 11; 87 /** 88 * The driver is started or stopped. The object will be the state: true for 89 * started, false for stopped. 90 */ 91 private static final int EVENT_DRIVER_STATE_CHANGED = 12; 92 private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT = 13; 93 private static final int EVENT_MAYBE_START_SCAN_POST_DISCONNECT = 14; 94 95 /** 96 * The driver state indication. 97 */ 98 private static final int DRIVER_STARTED = 0; 99 private static final int DRIVER_STOPPED = 1; 100 private static final int DRIVER_HUNG = 2; 101 102 /** 103 * Interval in milliseconds between polling for connection 104 * status items that are not sent via asynchronous events. 105 * An example is RSSI (signal strength). 106 */ 107 private static final int POLL_STATUS_INTERVAL_MSECS = 3000; 108 109 /** 110 * The max number of the WPA supplicant loop iterations before we 111 * decide that the loop should be terminated: 112 */ 113 private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4; 114 115 /** 116 * When a DISCONNECT event is received, we defer handling it to 117 * allow for the possibility that the DISCONNECT is about to 118 * be followed shortly by a CONNECT to the same network we were 119 * just connected to. In such a case, we don't want to report 120 * the network as down, nor do we want to reconfigure the network 121 * interface, etc. If we get a CONNECT event for another network 122 * within the delay window, we immediately handle the pending 123 * disconnect before processing the CONNECT.<p/> 124 * The five second delay is chosen somewhat arbitrarily, but is 125 * meant to cover most of the cases where a DISCONNECT/CONNECT 126 * happens to a network. 127 */ 128 private static final int DISCONNECT_DELAY_MSECS = 5000; 129 /** 130 * When the supplicant goes idle after we do an explicit disconnect 131 * following a DHCP failure, we need to kick the supplicant into 132 * trying to associate with access points. 133 */ 134 private static final int RECONNECT_DELAY_MSECS = 2000; 135 136 /** 137 * When the supplicant disconnects from an AP it sometimes forgets 138 * to restart scanning. Wait this delay before asking it to start 139 * scanning (in case it forgot). 15 sec is the standard delay between 140 * scans. 141 */ 142 private static final int KICKSTART_SCANNING_DELAY_MSECS = 15000; 143 144 /** 145 * The maximum number of times we will retry a connection to an access point 146 * for which we have failed in acquiring an IP address from DHCP. A value of 147 * N means that we will make N+1 connection attempts in all. 148 * <p> 149 * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default 150 * value if a Settings value is not present. 151 */ 152 private static final int DEFAULT_MAX_DHCP_RETRIES = 2; 153 154 private static final int DRIVER_POWER_MODE_AUTO = 0; 155 private static final int DRIVER_POWER_MODE_ACTIVE = 1; 156 157 /** 158 * The current WPA supplicant loop state (used to detect looping behavior): 159 */ 160 private SupplicantState mSupplicantLoopState = SupplicantState.DISCONNECTED; 161 162 /** 163 * The current number of WPA supplicant loop iterations: 164 */ 165 private int mNumSupplicantLoopIterations = 0; 166 167 /** 168 * The current number of supplicant state changes. This is used to determine 169 * if we've received any new info since we found out it was DISCONNECTED or 170 * INACTIVE. If we haven't for X ms, we then request a scan - it should have 171 * done that automatically, but sometimes some firmware does not. 172 */ 173 private int mNumSupplicantStateChanges = 0; 174 175 /** 176 * True if we received an event that that a password-key may be incorrect. 177 * If the next incoming supplicant state change event is DISCONNECT, 178 * broadcast a message that we have a possible password error and disable 179 * the network. 180 */ 181 private boolean mPasswordKeyMayBeIncorrect = false; 182 183 public static final int SUPPL_SCAN_HANDLING_NORMAL = 1; 184 public static final int SUPPL_SCAN_HANDLING_LIST_ONLY = 2; 185 186 private WifiMonitor mWifiMonitor; 187 private WifiInfo mWifiInfo; 188 private List<ScanResult> mScanResults; 189 private WifiManager mWM; 190 private boolean mHaveIpAddress; 191 private boolean mObtainingIpAddress; 192 private boolean mTornDownByConnMgr; 193 /** 194 * A DISCONNECT event has been received, but processing it 195 * is being deferred. 196 */ 197 private boolean mDisconnectPending; 198 /** 199 * An operation has been performed as a result of which we expect the next event 200 * will be a DISCONNECT. 201 */ 202 private boolean mDisconnectExpected; 203 private DhcpHandler mDhcpTarget; 204 private DhcpInfo mDhcpInfo; 205 private int mLastSignalLevel = -1; 206 private String mLastBssid; 207 private String mLastSsid; 208 private int mLastNetworkId = -1; 209 private boolean mUseStaticIp = false; 210 private int mReconnectCount; 211 212 // used to store the (non-persisted) num determined during device boot 213 // (from mcc or other phone info) before the driver is started. 214 private int mNumAllowedChannels = 0; 215 216 // Variables relating to the 'available networks' notification 217 218 /** 219 * The icon to show in the 'available networks' notification. This will also 220 * be the ID of the Notification given to the NotificationManager. 221 */ 222 private static final int ICON_NETWORKS_AVAILABLE = 223 com.android.internal.R.drawable.stat_notify_wifi_in_range; 224 /** 225 * When a notification is shown, we wait this amount before possibly showing it again. 226 */ 227 private final long NOTIFICATION_REPEAT_DELAY_MS; 228 /** 229 * Whether the user has set the setting to show the 'available networks' notification. 230 */ 231 private boolean mNotificationEnabled; 232 /** 233 * Observes the user setting to keep {@link #mNotificationEnabled} in sync. 234 */ 235 private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver; 236 /** 237 * The {@link System#currentTimeMillis()} must be at least this value for us 238 * to show the notification again. 239 */ 240 private long mNotificationRepeatTime; 241 /** 242 * The Notification object given to the NotificationManager. 243 */ 244 private Notification mNotification; 245 /** 246 * Whether the notification is being shown, as set by us. That is, if the 247 * user cancels the notification, we will not receive the callback so this 248 * will still be true. We only guarantee if this is false, then the 249 * notification is not showing. 250 */ 251 private boolean mNotificationShown; 252 /** 253 * The number of continuous scans that must occur before consider the 254 * supplicant in a scanning state. This allows supplicant to associate with 255 * remembered networks that are in the scan results. 256 */ 257 private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3; 258 /** 259 * The number of scans since the last network state change. When this 260 * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the 261 * supplicant to actually be scanning. When the network state changes to 262 * something other than scanning, we reset this to 0. 263 */ 264 private int mNumScansSinceNetworkStateChange; 265 /** 266 * Observes the static IP address settings. 267 */ 268 private SettingsObserver mSettingsObserver; 269 270 private boolean mIsScanModeActive; 271 private boolean mEnableRssiPolling; 272 273 // Wi-Fi run states: 274 private static final int RUN_STATE_STARTING = 1; 275 private static final int RUN_STATE_RUNNING = 2; 276 private static final int RUN_STATE_STOPPING = 3; 277 private static final int RUN_STATE_STOPPED = 4; 278 279 private static final String mRunStateNames[] = { 280 "Starting", 281 "Running", 282 "Stopping", 283 "Stopped" 284 }; 285 private int mRunState; 286 287 private final IBatteryStats mBatteryStats; 288 289 private boolean mIsScanOnly; 290 291 private BluetoothA2dp mBluetoothA2dp; 292 293 private String mInterfaceName; 294 private static String LS = System.getProperty("line.separator"); 295 296 private Runnable mReleaseWakeLockCallback; 297 298 private static String[] sDnsPropNames; 299 300 /** 301 * A structure for supplying information about a supplicant state 302 * change in the STATE_CHANGE event message that comes from the 303 * WifiMonitor 304 * thread. 305 */ 306 private static class SupplicantStateChangeResult { 307 SupplicantStateChangeResult(int networkId, SupplicantState state) { 308 this.state = state; 309 this.networkId = networkId; 310 } 311 int networkId; 312 SupplicantState state; 313 } 314 315 /** 316 * A structure for supplying information about a connection in 317 * the CONNECTED event message that comes from the WifiMonitor 318 * thread. 319 */ 320 private static class NetworkStateChangeResult { 321 NetworkStateChangeResult(DetailedState state, String BSSID, int networkId) { 322 this.state = state; 323 this.BSSID = BSSID; 324 this.networkId = networkId; 325 } 326 DetailedState state; 327 String BSSID; 328 int networkId; 329 } 330 331 public WifiStateTracker(Context context, Handler target) { 332 super(context, target, ConnectivityManager.TYPE_WIFI, 0, "WIFI", ""); 333 334 mWifiInfo = new WifiInfo(); 335 mWifiMonitor = new WifiMonitor(this); 336 mHaveIpAddress = false; 337 mObtainingIpAddress = false; 338 setTornDownByConnMgr(false); 339 mDisconnectPending = false; 340 mScanResults = new ArrayList<ScanResult>(); 341 // Allocate DHCP info object once, and fill it in on each request 342 mDhcpInfo = new DhcpInfo(); 343 mRunState = RUN_STATE_STARTING; 344 345 // Setting is in seconds 346 NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), 347 Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; 348 mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler()); 349 mNotificationEnabledSettingObserver.register(); 350 351 mSettingsObserver = new SettingsObserver(new Handler()); 352 353 mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0"); 354 sDnsPropNames = new String[] { 355 "dhcp." + mInterfaceName + ".dns1", 356 "dhcp." + mInterfaceName + ".dns2" 357 }; 358 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); 359 360 } 361 362 /** 363 * Helper method: sets the supplicant state and keeps the network 364 * info updated. 365 * @param state the new state 366 */ 367 private void setSupplicantState(SupplicantState state) { 368 mWifiInfo.setSupplicantState(state); 369 updateNetworkInfo(); 370 checkPollTimer(); 371 } 372 373 public SupplicantState getSupplicantState() { 374 return mWifiInfo.getSupplicantState(); 375 } 376 377 /** 378 * Helper method: sets the supplicant state and keeps the network 379 * info updated (string version). 380 * @param stateName the string name of the new state 381 */ 382 private void setSupplicantState(String stateName) { 383 mWifiInfo.setSupplicantState(stateName); 384 updateNetworkInfo(); 385 checkPollTimer(); 386 } 387 388 /** 389 * Helper method: sets the boolean indicating that the connection 390 * manager asked the network to be torn down (and so only the connection 391 * manager can set it up again). 392 * network info updated. 393 * @param flag {@code true} if explicitly disabled. 394 */ 395 private void setTornDownByConnMgr(boolean flag) { 396 mTornDownByConnMgr = flag; 397 updateNetworkInfo(); 398 } 399 400 /** 401 * Return the IP addresses of the DNS servers available for the WLAN 402 * network interface. 403 * @return a list of DNS addresses, with no holes. 404 */ 405 public String[] getNameServers() { 406 return getNameServerList(sDnsPropNames); 407 } 408 409 /** 410 * Return the name of our WLAN network interface. 411 * @return the name of our interface. 412 */ 413 public String getInterfaceName() { 414 return mInterfaceName; 415 } 416 417 /** 418 * Return the system properties name associated with the tcp buffer sizes 419 * for this network. 420 */ 421 public String getTcpBufferSizesPropName() { 422 return "net.tcp.buffersize.wifi"; 423 } 424 425 public void startMonitoring() { 426 /* 427 * Get a handle on the WifiManager. This cannot be done in our 428 * constructor, because the Wifi service is not yet registered. 429 */ 430 mWM = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE); 431 } 432 433 public void startEventLoop() { 434 mWifiMonitor.startMonitoring(); 435 } 436 437 /** 438 * Wi-Fi is considered available as long as we have a connection to the 439 * supplicant daemon and there is at least one enabled network. If a teardown 440 * was explicitly requested, then Wi-Fi can be restarted with a reconnect 441 * request, so it is considered available. If the driver has been stopped 442 * for any reason other than a teardown request, Wi-Fi is considered 443 * unavailable. 444 * @return {@code true} if Wi-Fi connections are possible 445 */ 446 public synchronized boolean isAvailable() { 447 /* 448 * TODO: Need to also look at scan results to see whether we're 449 * in range of any access points. If we have scan results that 450 * are no more than N seconds old, use those, otherwise, initiate 451 * a scan and wait for the results. This only matters if we 452 * allow mobile to be the preferred network. 453 */ 454 SupplicantState suppState = mWifiInfo.getSupplicantState(); 455 return suppState != SupplicantState.UNINITIALIZED && 456 suppState != SupplicantState.INACTIVE && 457 (mTornDownByConnMgr || !isDriverStopped()); 458 } 459 460 /** 461 * {@inheritDoc} 462 * There are currently no defined Wi-Fi subtypes. 463 */ 464 public int getNetworkSubtype() { 465 return 0; 466 } 467 468 /** 469 * Helper method: updates the network info object to keep it in sync with 470 * the Wi-Fi state tracker. 471 */ 472 private void updateNetworkInfo() { 473 mNetworkInfo.setIsAvailable(isAvailable()); 474 } 475 476 /** 477 * Report whether the Wi-Fi connection is fully configured for data. 478 * @return {@code true} if the {@link SupplicantState} is 479 * {@link android.net.wifi.SupplicantState#COMPLETED COMPLETED}. 480 */ 481 public boolean isConnectionCompleted() { 482 return mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED; 483 } 484 485 /** 486 * Report whether the Wi-Fi connection has successfully acquired an IP address. 487 * @return {@code true} if the Wi-Fi connection has been assigned an IP address. 488 */ 489 public boolean hasIpAddress() { 490 return mHaveIpAddress; 491 } 492 493 /** 494 * Send the tracker a notification that a user-entered password key 495 * may be incorrect (i.e., caused authentication to fail). 496 */ 497 void notifyPasswordKeyMayBeIncorrect() { 498 sendEmptyMessage(EVENT_PASSWORD_KEY_MAY_BE_INCORRECT); 499 } 500 501 /** 502 * Send the tracker a notification that a connection to the supplicant 503 * daemon has been established. 504 */ 505 void notifySupplicantConnection() { 506 sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION); 507 } 508 509 /** 510 * Send the tracker a notification that the state of the supplicant 511 * has changed. 512 * @param networkId the configured network on which the state change occurred 513 * @param newState the new {@code SupplicantState} 514 */ 515 void notifyStateChange(int networkId, SupplicantState newState) { 516 Message msg = Message.obtain( 517 this, EVENT_SUPPLICANT_STATE_CHANGED, 518 new SupplicantStateChangeResult(networkId, newState)); 519 msg.sendToTarget(); 520 } 521 522 /** 523 * Send the tracker a notification that the state of Wifi connectivity 524 * has changed. 525 * @param networkId the configured network on which the state change occurred 526 * @param newState the new network state 527 * @param BSSID when the new state is {@link DetailedState#CONNECTED 528 * NetworkInfo.DetailedState.CONNECTED}, 529 * this is the MAC address of the access point. Otherwise, it 530 * is {@code null}. 531 */ 532 void notifyStateChange(DetailedState newState, String BSSID, int networkId) { 533 Message msg = Message.obtain( 534 this, EVENT_NETWORK_STATE_CHANGED, 535 new NetworkStateChangeResult(newState, BSSID, networkId)); 536 msg.sendToTarget(); 537 } 538 539 /** 540 * Send the tracker a notification that a scan has completed, and results 541 * are available. 542 */ 543 void notifyScanResultsAvailable() { 544 // reset the supplicant's handling of scan results to "normal" mode 545 synchronized (this) { 546 WifiNative.setScanResultHandlingCommand(SUPPL_SCAN_HANDLING_NORMAL); 547 } 548 sendEmptyMessage(EVENT_SCAN_RESULTS_AVAILABLE); 549 } 550 551 /** 552 * Send the tracker a notification that we can no longer communicate with 553 * the supplicant daemon. 554 */ 555 void notifySupplicantLost() { 556 sendEmptyMessage(EVENT_SUPPLICANT_DISCONNECT); 557 } 558 559 /** 560 * Send the tracker a notification that the Wi-Fi driver has been stopped. 561 */ 562 void notifyDriverStopped() { 563 mRunState = RUN_STATE_STOPPED; 564 565 // Send a driver stopped message to our handler 566 Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STOPPED, 0).sendToTarget(); 567 } 568 569 /** 570 * Send the tracker a notification that the Wi-Fi driver has been restarted after 571 * having been stopped. 572 */ 573 void notifyDriverStarted() { 574 // Send a driver started message to our handler 575 Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STARTED, 0).sendToTarget(); 576 } 577 578 /** 579 * Send the tracker a notification that the Wi-Fi driver has hung and needs restarting. 580 */ 581 void notifyDriverHung() { 582 // Send a driver hanged message to our handler 583 Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_HUNG, 0).sendToTarget(); 584 } 585 586 /** 587 * Set the interval timer for polling connection information 588 * that is not delivered asynchronously. 589 */ 590 private synchronized void checkPollTimer() { 591 if (mEnableRssiPolling && 592 mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED && 593 !hasMessages(EVENT_POLL_INTERVAL)) { 594 sendEmptyMessageDelayed(EVENT_POLL_INTERVAL, POLL_STATUS_INTERVAL_MSECS); 595 } 596 } 597 598 private synchronized boolean isDriverStopped() { 599 return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING; 600 } 601 602 private void noteRunState() { 603 try { 604 if (mRunState == RUN_STATE_RUNNING) { 605 mBatteryStats.noteWifiRunning(); 606 } else if (mRunState == RUN_STATE_STOPPED) { 607 mBatteryStats.noteWifiStopped(); 608 } 609 } catch (RemoteException ignore) { 610 } 611 } 612 613 /** 614 * Set the number of allowed radio frequency channels from the system 615 * setting value, if any. 616 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., 617 * the number of channels is invalid. 618 */ 619 public boolean setNumAllowedChannels() { 620 try { 621 return setNumAllowedChannels( 622 Settings.Secure.getInt(mContext.getContentResolver(), 623 Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS)); 624 } catch (Settings.SettingNotFoundException e) { 625 if (mNumAllowedChannels != 0) { 626 WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels); 627 } 628 // otherwise, use the driver default 629 } 630 return true; 631 } 632 633 /** 634 * Set the number of radio frequency channels that are allowed to be used 635 * in the current regulatory domain. 636 * @param numChannels the number of allowed channels. Must be greater than 0 637 * and less than or equal to 16. 638 * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., 639 * {@code numChannels} is outside the valid range. 640 */ 641 public synchronized boolean setNumAllowedChannels(int numChannels) { 642 mNumAllowedChannels = numChannels; 643 return WifiNative.setNumAllowedChannelsCommand(numChannels); 644 } 645 646 /** 647 * Set the run state to either "normal" or "scan-only". 648 * @param scanOnlyMode true if the new mode should be scan-only. 649 */ 650 public synchronized void setScanOnlyMode(boolean scanOnlyMode) { 651 // do nothing unless scan-only mode is changing 652 if (mIsScanOnly != scanOnlyMode) { 653 int scanType = (scanOnlyMode ? 654 SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL); 655 if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType); 656 if (WifiNative.setScanResultHandlingCommand(scanType)) { 657 mIsScanOnly = scanOnlyMode; 658 if (!isDriverStopped()) { 659 if (scanOnlyMode) { 660 WifiNative.disconnectCommand(); 661 } else { 662 WifiNative.reconnectCommand(); 663 } 664 } 665 } 666 } 667 } 668 669 /** 670 * Enable or disable Bluetooth coexistence scan mode. When this mode is on, 671 * some of the low-level scan parameters used by the driver are changed to 672 * reduce interference with A2DP streaming. 673 * 674 * @param isBluetoothPlaying whether to enable or disable this mode 675 */ 676 public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) { 677 WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying); 678 } 679 680 private void checkIsBluetoothPlaying() { 681 boolean isBluetoothPlaying = false; 682 Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks(); 683 684 for (BluetoothDevice device : connected) { 685 if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) { 686 isBluetoothPlaying = true; 687 break; 688 } 689 } 690 setBluetoothScanMode(isBluetoothPlaying); 691 } 692 693 public void enableRssiPolling(boolean enable) { 694 if (mEnableRssiPolling != enable) { 695 mEnableRssiPolling = enable; 696 checkPollTimer(); 697 } 698 } 699 700 @Override 701 public void releaseWakeLock() { 702 if (mReleaseWakeLockCallback != null) { 703 mReleaseWakeLockCallback.run(); 704 } 705 } 706 707 public void setReleaseWakeLockCallback(Runnable callback) { 708 mReleaseWakeLockCallback = callback; 709 } 710 711 /** 712 * Tracks the WPA supplicant states to detect "loop" situations. 713 * @param newSupplicantState The new WPA supplicant state. 714 * @return {@code true} if the supplicant loop should be stopped 715 * and {@code false} if it should continue. 716 */ 717 private boolean isSupplicantLooping(SupplicantState newSupplicantState) { 718 if (SupplicantState.ASSOCIATING.ordinal() <= newSupplicantState.ordinal() 719 && newSupplicantState.ordinal() < SupplicantState.COMPLETED.ordinal()) { 720 if (mSupplicantLoopState != newSupplicantState) { 721 if (newSupplicantState.ordinal() < mSupplicantLoopState.ordinal()) { 722 ++mNumSupplicantLoopIterations; 723 } 724 725 mSupplicantLoopState = newSupplicantState; 726 } 727 } else if (newSupplicantState == SupplicantState.COMPLETED) { 728 resetSupplicantLoopState(); 729 } 730 731 return mNumSupplicantLoopIterations >= MAX_SUPPLICANT_LOOP_ITERATIONS; 732 } 733 734 /** 735 * Resets the WPA supplicant loop state. 736 */ 737 private void resetSupplicantLoopState() { 738 mNumSupplicantLoopIterations = 0; 739 } 740 741 @Override 742 public void handleMessage(Message msg) { 743 Intent intent; 744 745 switch (msg.what) { 746 case EVENT_SUPPLICANT_CONNECTION: 747 mRunState = RUN_STATE_RUNNING; 748 noteRunState(); 749 checkUseStaticIp(); 750 /* 751 * DHCP requests are blocking, so run them in a separate thread. 752 */ 753 HandlerThread dhcpThread = new HandlerThread("DHCP Handler Thread"); 754 dhcpThread.start(); 755 mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this); 756 mIsScanModeActive = true; 757 mTornDownByConnMgr = false; 758 mLastBssid = null; 759 mLastSsid = null; 760 requestConnectionInfo(); 761 SupplicantState supplState = mWifiInfo.getSupplicantState(); 762 /** 763 * The MAC address isn't going to change, so just request it 764 * once here. 765 */ 766 String macaddr; 767 synchronized (this) { 768 macaddr = WifiNative.getMacAddressCommand(); 769 } 770 if (macaddr != null) { 771 mWifiInfo.setMacAddress(macaddr); 772 } 773 if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant established, state=" + 774 supplState); 775 // Wi-Fi supplicant connection state changed: 776 // [31- 2] Reserved for future use 777 // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) , 778 // or supplicant died (2) 779 EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, 1); 780 /* 781 * The COMPLETED state change from the supplicant may have occurred 782 * in between polling for supplicant availability, in which case 783 * we didn't perform a DHCP request to get an IP address. 784 */ 785 if (supplState == SupplicantState.COMPLETED) { 786 mLastBssid = mWifiInfo.getBSSID(); 787 mLastSsid = mWifiInfo.getSSID(); 788 configureInterface(); 789 } 790 if (ActivityManagerNative.isSystemReady()) { 791 intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); 792 intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, true); 793 mContext.sendBroadcast(intent); 794 } 795 if (supplState == SupplicantState.COMPLETED && mHaveIpAddress) { 796 setDetailedState(DetailedState.CONNECTED); 797 } else { 798 setDetailedState(WifiInfo.getDetailedStateOf(supplState)); 799 } 800 /* 801 * Filter out multicast packets. This saves battery power, since 802 * the CPU doesn't have to spend time processing packets that 803 * are going to end up being thrown away. 804 * 805 * Note that rather than turn this off directly, we use the 806 * public api - this keeps us all in sync - turn multicast on 807 * first and then off.. if nobody else wants it on it'll be 808 * off then and it's all synchronized within the API. 809 */ 810 WifiManager.MulticastLock l = 811 mWM.createMulticastLock("WifiStateTracker"); 812 l.acquire(); 813 l.release(); 814 815 if (mBluetoothA2dp == null) { 816 mBluetoothA2dp = new BluetoothA2dp(mContext); 817 } 818 checkIsBluetoothPlaying(); 819 820 // initialize this after the supplicant is alive 821 setNumAllowedChannels(); 822 break; 823 824 case EVENT_SUPPLICANT_DISCONNECT: 825 mRunState = RUN_STATE_STOPPED; 826 noteRunState(); 827 int wifiState = mWM.getWifiState(); 828 boolean died = wifiState != WifiManager.WIFI_STATE_DISABLED && 829 wifiState != WifiManager.WIFI_STATE_DISABLING; 830 if (died) { 831 if (LOCAL_LOGD) Log.v(TAG, "Supplicant died unexpectedly"); 832 } else { 833 if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant lost"); 834 } 835 // Wi-Fi supplicant connection state changed: 836 // [31- 2] Reserved for future use 837 // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) , 838 // or supplicant died (2) 839 EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, died ? 2 : 0); 840 synchronized (this) { 841 WifiNative.closeSupplicantConnection(); 842 } 843 if (died) { 844 resetInterface(false); 845 } 846 // When supplicant dies, kill the DHCP thread 847 if (mDhcpTarget != null) { 848 mDhcpTarget.getLooper().quit(); 849 mDhcpTarget = null; 850 } 851 mContext.removeStickyBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION)); 852 if (ActivityManagerNative.isSystemReady()) { 853 intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); 854 intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false); 855 mContext.sendBroadcast(intent); 856 } 857 setDetailedState(DetailedState.DISCONNECTED); 858 setSupplicantState(SupplicantState.UNINITIALIZED); 859 mHaveIpAddress = false; 860 mObtainingIpAddress = false; 861 if (died) { 862 mWM.setWifiEnabled(false); 863 } 864 break; 865 866 case EVENT_MAYBE_START_SCAN_POST_DISCONNECT: 867 // Only do this if we haven't gotten a new supplicant status since the timer 868 // started 869 if (mNumSupplicantStateChanges == msg.arg1) { 870 WifiNative.scanCommand(false); // do a passive scan 871 } 872 break; 873 874 case EVENT_SUPPLICANT_STATE_CHANGED: 875 mNumSupplicantStateChanges++; 876 SupplicantStateChangeResult supplicantStateResult = 877 (SupplicantStateChangeResult) msg.obj; 878 SupplicantState newState = supplicantStateResult.state; 879 SupplicantState currentState = mWifiInfo.getSupplicantState(); 880 881 // Wi-Fi supplicant state changed: 882 // [31- 6] Reserved for future use 883 // [ 5- 0] Supplicant state ordinal (as defined by SupplicantState) 884 int eventLogParam = (newState.ordinal() & 0x3f); 885 EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, eventLogParam); 886 887 if (LOCAL_LOGD) Log.v(TAG, "Changing supplicant state: " 888 + currentState + 889 " ==> " + newState); 890 891 int networkId = supplicantStateResult.networkId; 892 893 /* 894 * If we get disconnect or inactive we need to start our 895 * watchdog timer to start a scan 896 */ 897 if (newState == SupplicantState.DISCONNECTED || 898 newState == SupplicantState.INACTIVE) { 899 sendMessageDelayed(obtainMessage(EVENT_MAYBE_START_SCAN_POST_DISCONNECT, 900 mNumSupplicantStateChanges, 0), KICKSTART_SCANNING_DELAY_MSECS); 901 } 902 903 904 /* 905 * Did we get to DISCONNECTED state due to an 906 * authentication (password) failure? 907 */ 908 boolean failedToAuthenticate = false; 909 if (newState == SupplicantState.DISCONNECTED) { 910 failedToAuthenticate = mPasswordKeyMayBeIncorrect; 911 } 912 mPasswordKeyMayBeIncorrect = false; 913 914 /* 915 * Keep track of the supplicant state and check if we should 916 * disable the network 917 */ 918 boolean disabledNetwork = false; 919 if (isSupplicantLooping(newState)) { 920 if (LOCAL_LOGD) { 921 Log.v(TAG, 922 "Stop WPA supplicant loop and disable network"); 923 } 924 disabledNetwork = wifiManagerDisableNetwork(networkId); 925 } 926 927 if (disabledNetwork) { 928 /* 929 * Reset the loop state if we disabled the network 930 */ 931 resetSupplicantLoopState(); 932 } else if (newState != currentState || 933 (newState == SupplicantState.DISCONNECTED && isDriverStopped())) { 934 setSupplicantState(newState); 935 if (newState == SupplicantState.DORMANT) { 936 DetailedState newDetailedState; 937 if (mIsScanOnly || mRunState == RUN_STATE_STOPPING) { 938 newDetailedState = DetailedState.IDLE; 939 } else { 940 newDetailedState = DetailedState.FAILED; 941 } 942 handleDisconnectedState(newDetailedState); 943 /** 944 * If we were associated with a network (networkId != -1), 945 * assume we reached this state because of a failed attempt 946 * to acquire an IP address, and attempt another connection 947 * and IP address acquisition in RECONNECT_DELAY_MSECS 948 * milliseconds. 949 */ 950 if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly && networkId != -1) { 951 sendEmptyMessageDelayed(EVENT_DEFERRED_RECONNECT, RECONNECT_DELAY_MSECS); 952 } else if (mRunState == RUN_STATE_STOPPING) { 953 synchronized (this) { 954 WifiNative.stopDriverCommand(); 955 } 956 } else if (mRunState == RUN_STATE_STARTING && !mIsScanOnly) { 957 synchronized (this) { 958 WifiNative.reconnectCommand(); 959 } 960 } 961 } else if (newState == SupplicantState.DISCONNECTED) { 962 mHaveIpAddress = false; 963 if (isDriverStopped() || mDisconnectExpected) { 964 handleDisconnectedState(DetailedState.DISCONNECTED); 965 } else { 966 scheduleDisconnect(); 967 } 968 } else if (newState != SupplicantState.COMPLETED && !mDisconnectPending) { 969 /** 970 * Ignore events that don't change the connectivity state, 971 * such as WPA rekeying operations. 972 */ 973 if (!(currentState == SupplicantState.COMPLETED && 974 (newState == SupplicantState.ASSOCIATING || 975 newState == SupplicantState.ASSOCIATED || 976 newState == SupplicantState.FOUR_WAY_HANDSHAKE || 977 newState == SupplicantState.GROUP_HANDSHAKE))) { 978 setDetailedState(WifiInfo.getDetailedStateOf(newState)); 979 } 980 } 981 982 mDisconnectExpected = false; 983 intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 984 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 985 intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)newState); 986 if (failedToAuthenticate) { 987 if (LOCAL_LOGD) Log.d(TAG, "Failed to authenticate, disabling network " + networkId); 988 wifiManagerDisableNetwork(networkId); 989 intent.putExtra( 990 WifiManager.EXTRA_SUPPLICANT_ERROR, 991 WifiManager.ERROR_AUTHENTICATING); 992 } 993 mContext.sendStickyBroadcast(intent); 994 } 995 break; 996 997 case EVENT_NETWORK_STATE_CHANGED: 998 /* 999 * Each CONNECT or DISCONNECT generates a pair of events. 1000 * One is a supplicant state change event, and the other 1001 * is a network state change event. For connects, the 1002 * supplicant event always arrives first, followed by 1003 * the network state change event. Only the latter event 1004 * has the BSSID, which we are interested in capturing. 1005 * For disconnects, the order is the opposite -- the 1006 * network state change event comes first, followed by 1007 * the supplicant state change event. 1008 */ 1009 NetworkStateChangeResult result = 1010 (NetworkStateChangeResult) msg.obj; 1011 1012 // Wi-Fi network state changed: 1013 // [31- 6] Reserved for future use 1014 // [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState) 1015 eventLogParam = (result.state.ordinal() & 0x3f); 1016 EventLog.writeEvent(EVENTLOG_NETWORK_STATE_CHANGED, eventLogParam); 1017 1018 if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state); 1019 /* 1020 * If we're in scan-only mode, don't advance the state machine, and 1021 * don't report the state change to clients. 1022 */ 1023 if (mIsScanOnly) { 1024 if (LOCAL_LOGD) Log.v(TAG, "Dropping event in scan-only mode"); 1025 break; 1026 } 1027 if (result.state != DetailedState.SCANNING) { 1028 /* 1029 * Reset the scan count since there was a network state 1030 * change. This could be from supplicant trying to associate 1031 * with a network. 1032 */ 1033 mNumScansSinceNetworkStateChange = 0; 1034 } 1035 /* 1036 * If the supplicant sent us a CONNECTED event, we don't 1037 * want to send out an indication of overall network 1038 * connectivity until we have our IP address. If the 1039 * supplicant sent us a DISCONNECTED event, we delay 1040 * sending a notification in case a reconnection to 1041 * the same access point occurs within a short time. 1042 */ 1043 if (result.state == DetailedState.DISCONNECTED) { 1044 if (mWifiInfo.getSupplicantState() != SupplicantState.DORMANT) { 1045 scheduleDisconnect(); 1046 } 1047 break; 1048 } 1049 requestConnectionStatus(mWifiInfo); 1050 if (!(result.state == DetailedState.CONNECTED && 1051 (!mHaveIpAddress || mDisconnectPending))) { 1052 setDetailedState(result.state); 1053 } 1054 1055 if (result.state == DetailedState.CONNECTED) { 1056 /* 1057 * Remove the 'available networks' notification when we 1058 * successfully connect to a network. 1059 */ 1060 setNotificationVisible(false, 0, false, 0); 1061 boolean wasDisconnectPending = mDisconnectPending; 1062 cancelDisconnect(); 1063 /* 1064 * The connection is fully configured as far as link-level 1065 * connectivity is concerned, but we may still need to obtain 1066 * an IP address. 1067 */ 1068 if (wasDisconnectPending) { 1069 DetailedState saveState = getNetworkInfo().getDetailedState(); 1070 handleDisconnectedState(DetailedState.DISCONNECTED); 1071 setDetailedStateInternal(saveState); 1072 } 1073 configureInterface(); 1074 mLastBssid = result.BSSID; 1075 mLastSsid = mWifiInfo.getSSID(); 1076 mLastNetworkId = result.networkId; 1077 if (mHaveIpAddress) { 1078 setDetailedState(DetailedState.CONNECTED); 1079 } else { 1080 setDetailedState(DetailedState.OBTAINING_IPADDR); 1081 } 1082 } 1083 sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID()); 1084 break; 1085 1086 case EVENT_SCAN_RESULTS_AVAILABLE: 1087 if (ActivityManagerNative.isSystemReady()) { 1088 mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); 1089 } 1090 sendScanResultsAvailable(); 1091 /** 1092 * On receiving the first scan results after connecting to 1093 * the supplicant, switch scan mode over to passive. 1094 */ 1095 setScanMode(false); 1096 break; 1097 1098 case EVENT_POLL_INTERVAL: 1099 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) { 1100 requestPolledInfo(mWifiInfo, true); 1101 checkPollTimer(); 1102 } 1103 break; 1104 1105 case EVENT_DEFERRED_DISCONNECT: 1106 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) { 1107 handleDisconnectedState(DetailedState.DISCONNECTED); 1108 } 1109 break; 1110 1111 case EVENT_DEFERRED_RECONNECT: 1112 /* 1113 * If we've exceeded the maximum number of retries for reconnecting 1114 * to a given network, disable the network so that the supplicant 1115 * will try some other network, if any is available. 1116 * TODO: network ID may have changed since we stored it. 1117 */ 1118 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) { 1119 if (++mReconnectCount > getMaxDhcpRetries()) { 1120 mWM.disableNetwork(mLastNetworkId); 1121 } 1122 synchronized(this) { 1123 WifiNative.reconnectCommand(); 1124 } 1125 } 1126 break; 1127 1128 case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED: 1129 /** 1130 * Since this event is sent from another thread, it might have been 1131 * sent after we closed our connection to the supplicant in the course 1132 * of disabling Wi-Fi. In that case, we should just ignore the event. 1133 */ 1134 if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) { 1135 break; 1136 } 1137 mReconnectCount = 0; 1138 mHaveIpAddress = true; 1139 mObtainingIpAddress = false; 1140 mWifiInfo.setIpAddress(mDhcpInfo.ipAddress); 1141 mLastSignalLevel = -1; // force update of signal strength 1142 if (mNetworkInfo.getDetailedState() != DetailedState.CONNECTED) { 1143 setDetailedState(DetailedState.CONNECTED); 1144 sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID()); 1145 } else { 1146 mTarget.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED); 1147 } 1148 if (LOCAL_LOGD) Log.v(TAG, "IP configuration: " + mDhcpInfo); 1149 // Wi-Fi interface configuration state changed: 1150 // [31- 1] Reserved for future use 1151 // [ 0- 0] Interface configuration succeeded (1) or failed (0) 1152 EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 1); 1153 1154 // We've connected successfully, so allow the notification again in the future 1155 resetNotificationTimer(); 1156 break; 1157 1158 case EVENT_INTERFACE_CONFIGURATION_FAILED: 1159 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) { 1160 // Wi-Fi interface configuration state changed: 1161 // [31- 1] Reserved for future use 1162 // [ 0- 0] Interface configuration succeeded (1) or failed (0) 1163 EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 0); 1164 1165 mHaveIpAddress = false; 1166 mWifiInfo.setIpAddress(0); 1167 mObtainingIpAddress = false; 1168 synchronized(this) { 1169 WifiNative.disconnectCommand(); 1170 } 1171 } 1172 break; 1173 1174 case EVENT_DRIVER_STATE_CHANGED: 1175 // Wi-Fi driver state changed: 1176 // 0 STARTED 1177 // 1 STOPPED 1178 // 2 HUNG 1179 EventLog.writeEvent(EVENTLOG_DRIVER_STATE_CHANGED, msg.arg1); 1180 1181 switch (msg.arg1) { 1182 case DRIVER_STARTED: 1183 /** 1184 * Set the number of allowed radio channels according 1185 * to the system setting, since it gets reset by the 1186 * driver upon changing to the STARTED state. 1187 */ 1188 setNumAllowedChannels(); 1189 synchronized (this) { 1190 if (mRunState == RUN_STATE_STARTING) { 1191 mRunState = RUN_STATE_RUNNING; 1192 if (!mIsScanOnly) { 1193 WifiNative.reconnectCommand(); 1194 } else { 1195 // In some situations, supplicant needs to be kickstarted to 1196 // start the background scanning 1197 WifiNative.scanCommand(true); 1198 } 1199 } 1200 } 1201 break; 1202 case DRIVER_HUNG: 1203 Log.e(TAG, "Wifi Driver reports HUNG - reloading."); 1204 /** 1205 * restart the driver - toggle off and on 1206 */ 1207 mWM.setWifiEnabled(false); 1208 mWM.setWifiEnabled(true); 1209 break; 1210 } 1211 noteRunState(); 1212 break; 1213 1214 case EVENT_PASSWORD_KEY_MAY_BE_INCORRECT: 1215 mPasswordKeyMayBeIncorrect = true; 1216 break; 1217 } 1218 } 1219 1220 private boolean wifiManagerDisableNetwork(int networkId) { 1221 boolean disabledNetwork = false; 1222 if (0 <= networkId) { 1223 disabledNetwork = mWM.disableNetwork(networkId); 1224 if (LOCAL_LOGD) { 1225 if (disabledNetwork) { 1226 Log.v(TAG, "Disabled network: " + networkId); 1227 } 1228 } 1229 } 1230 if (LOCAL_LOGD) { 1231 if (!disabledNetwork) { 1232 Log.e(TAG, "Failed to disable network:" + 1233 " invalid network id: " + networkId); 1234 } 1235 } 1236 return disabledNetwork; 1237 } 1238 1239 public synchronized void setScanMode(boolean isScanModeActive) { 1240 if (mIsScanModeActive != isScanModeActive) { 1241 WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive); 1242 } 1243 } 1244 1245 private void configureInterface() { 1246 checkPollTimer(); 1247 mLastSignalLevel = -1; 1248 if (!mUseStaticIp) { 1249 if (!mHaveIpAddress && !mObtainingIpAddress) { 1250 mObtainingIpAddress = true; 1251 mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START); 1252 } 1253 } else { 1254 int event; 1255 if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) { 1256 mHaveIpAddress = true; 1257 event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED; 1258 if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration succeeded"); 1259 } else { 1260 mHaveIpAddress = false; 1261 event = EVENT_INTERFACE_CONFIGURATION_FAILED; 1262 if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed"); 1263 } 1264 sendEmptyMessage(event); 1265 } 1266 } 1267 1268 /** 1269 * Reset our IP state and send out broadcasts following a disconnect. 1270 * @param newState the {@code DetailedState} to set. Should be either 1271 * {@code DISCONNECTED} or {@code FAILED}. 1272 */ 1273 private void handleDisconnectedState(DetailedState newState) { 1274 if (LOCAL_LOGD) Log.d(TAG, "Deconfiguring interface and stopping DHCP"); 1275 if (mDisconnectPending) { 1276 cancelDisconnect(); 1277 } 1278 mDisconnectExpected = false; 1279 resetInterface(true); 1280 setDetailedState(newState); 1281 sendNetworkStateChangeBroadcast(mLastBssid); 1282 mWifiInfo.setBSSID(null); 1283 mLastBssid = null; 1284 mLastSsid = null; 1285 mDisconnectPending = false; 1286 } 1287 1288 /** 1289 * Resets the Wi-Fi interface by clearing any state, resetting any sockets 1290 * using the interface, stopping DHCP, and disabling the interface. 1291 */ 1292 public void resetInterface(boolean reenable) { 1293 mHaveIpAddress = false; 1294 mObtainingIpAddress = false; 1295 mWifiInfo.setIpAddress(0); 1296 1297 /* 1298 * Reset connection depends on both the interface and the IP assigned, 1299 * so it should be done before any chance of the IP being lost. 1300 */ 1301 NetworkUtils.resetConnections(mInterfaceName); 1302 1303 // Stop DHCP 1304 if (mDhcpTarget != null) { 1305 mDhcpTarget.setCancelCallback(true); 1306 mDhcpTarget.removeMessages(EVENT_DHCP_START); 1307 } 1308 if (!NetworkUtils.stopDhcp(mInterfaceName)) { 1309 Log.e(TAG, "Could not stop DHCP"); 1310 } 1311 1312 NetworkUtils.disableInterface(mInterfaceName); 1313 // we no longer net to start the interface (driver does this for us) 1314 // and it led to problems - removed. 1315 } 1316 1317 /** 1318 * The supplicant is reporting that we are disconnected from the current 1319 * access point. Often, however, a disconnect will be followed very shortly 1320 * by a reconnect to the same access point. Therefore, we delay resetting 1321 * the connection's IP state for a bit. 1322 */ 1323 private void scheduleDisconnect() { 1324 mDisconnectPending = true; 1325 if (!hasMessages(EVENT_DEFERRED_DISCONNECT)) { 1326 sendEmptyMessageDelayed(EVENT_DEFERRED_DISCONNECT, DISCONNECT_DELAY_MSECS); 1327 } 1328 } 1329 1330 private void cancelDisconnect() { 1331 mDisconnectPending = false; 1332 removeMessages(EVENT_DEFERRED_DISCONNECT); 1333 } 1334 1335 public DhcpInfo getDhcpInfo() { 1336 return mDhcpInfo; 1337 } 1338 1339 public synchronized List<ScanResult> getScanResultsList() { 1340 return mScanResults; 1341 } 1342 1343 public synchronized void setScanResultsList(List<ScanResult> scanList) { 1344 mScanResults = scanList; 1345 } 1346 1347 /** 1348 * Get status information for the current connection, if any. 1349 * @return a {@link WifiInfo} object containing information about the current connection 1350 */ 1351 public WifiInfo requestConnectionInfo() { 1352 requestConnectionStatus(mWifiInfo); 1353 requestPolledInfo(mWifiInfo, false); 1354 return mWifiInfo; 1355 } 1356 1357 private void requestConnectionStatus(WifiInfo info) { 1358 String reply; 1359 synchronized (this) { 1360 reply = WifiNative.statusCommand(); 1361 } 1362 if (reply == null) { 1363 return; 1364 } 1365 /* 1366 * Parse the reply from the supplicant to the status command, and update 1367 * local state accordingly. The reply is a series of lines of the form 1368 * "name=value". 1369 */ 1370 String SSID = null; 1371 String BSSID = null; 1372 String suppState = null; 1373 int netId = -1; 1374 String[] lines = reply.split("\n"); 1375 for (String line : lines) { 1376 String[] prop = line.split(" *= *", 2); 1377 if (prop.length < 2) 1378 continue; 1379 String name = prop[0]; 1380 String value = prop[1]; 1381 if (name.equalsIgnoreCase("id")) 1382 netId = Integer.parseInt(value); 1383 else if (name.equalsIgnoreCase("ssid")) 1384 SSID = value; 1385 else if (name.equalsIgnoreCase("bssid")) 1386 BSSID = value; 1387 else if (name.equalsIgnoreCase("wpa_state")) 1388 suppState = value; 1389 } 1390 info.setNetworkId(netId); 1391 info.setSSID(SSID); 1392 info.setBSSID(BSSID); 1393 /* 1394 * We only set the supplicant state if the previous state was 1395 * UNINITIALIZED. This should only happen when we first connect to 1396 * the supplicant. Once we're connected, we should always receive 1397 * an event upon any state change, but in this case, we want to 1398 * make sure any listeners are made aware of the state change. 1399 */ 1400 if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED && suppState != null) 1401 setSupplicantState(suppState); 1402 } 1403 1404 /** 1405 * Get the dynamic information that is not reported via events. 1406 * @param info the object into which the information should be captured. 1407 */ 1408 private synchronized void requestPolledInfo(WifiInfo info, boolean polling) 1409 { 1410 int newRssi = (polling ? WifiNative.getRssiApproxCommand() : WifiNative.getRssiCommand()); 1411 if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values 1412 /* some implementations avoid negative values by adding 256 1413 * so we need to adjust for that here. 1414 */ 1415 if (newRssi > 0) newRssi -= 256; 1416 info.setRssi(newRssi); 1417 /* 1418 * Rather then sending the raw RSSI out every time it 1419 * changes, we precalculate the signal level that would 1420 * be displayed in the status bar, and only send the 1421 * broadcast if that much more coarse-grained number 1422 * changes. This cuts down greatly on the number of 1423 * broadcasts, at the cost of not informing others 1424 * interested in RSSI of all the changes in signal 1425 * level. 1426 */ 1427 // TODO: The second arg to the call below needs to be a symbol somewhere, but 1428 // it's actually the size of an array of icons that's private 1429 // to StatusBar Policy. 1430 int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4); 1431 if (newSignalLevel != mLastSignalLevel) { 1432 sendRssiChangeBroadcast(newRssi); 1433 } 1434 mLastSignalLevel = newSignalLevel; 1435 } else { 1436 info.setRssi(-200); 1437 } 1438 int newLinkSpeed = WifiNative.getLinkSpeedCommand(); 1439 if (newLinkSpeed != -1) { 1440 info.setLinkSpeed(newLinkSpeed); 1441 } 1442 } 1443 1444 private void sendRssiChangeBroadcast(final int newRssi) { 1445 if (ActivityManagerNative.isSystemReady()) { 1446 Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION); 1447 intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi); 1448 mContext.sendBroadcast(intent); 1449 } 1450 } 1451 1452 private void sendNetworkStateChangeBroadcast(String bssid) { 1453 Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); 1454 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1455 intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo); 1456 if (bssid != null) 1457 intent.putExtra(WifiManager.EXTRA_BSSID, bssid); 1458 mContext.sendStickyBroadcast(intent); 1459 } 1460 1461 /** 1462 * Disable Wi-Fi connectivity by stopping the driver. 1463 */ 1464 public boolean teardown() { 1465 if (!mTornDownByConnMgr) { 1466 if (disconnectAndStop()) { 1467 setTornDownByConnMgr(true); 1468 return true; 1469 } else { 1470 return false; 1471 } 1472 } else { 1473 return true; 1474 } 1475 } 1476 1477 /** 1478 * Reenable Wi-Fi connectivity by restarting the driver. 1479 */ 1480 public boolean reconnect() { 1481 if (mTornDownByConnMgr) { 1482 if (restart()) { 1483 setTornDownByConnMgr(false); 1484 return true; 1485 } else { 1486 return false; 1487 } 1488 } else { 1489 return true; 1490 } 1491 } 1492 1493 /** 1494 * We want to stop the driver, but if we're connected to a network, 1495 * we first want to disconnect, so that the supplicant is always in 1496 * a known state (DISCONNECTED) when the driver is stopped. 1497 * @return {@code true} if the operation succeeds, which means that the 1498 * disconnect or stop command was initiated. 1499 */ 1500 public synchronized boolean disconnectAndStop() { 1501 if (mRunState != RUN_STATE_STOPPING && mRunState != RUN_STATE_STOPPED) { 1502 // Take down any open network notifications 1503 setNotificationVisible(false, 0, false, 0); 1504 1505 mRunState = RUN_STATE_STOPPING; 1506 if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) { 1507 return WifiNative.stopDriverCommand(); 1508 } else { 1509 return WifiNative.disconnectCommand(); 1510 } 1511 } else { 1512 /* 1513 * The "driver-stop" wake lock normally is released from the 1514 * connectivity manager after the mobile data connection has 1515 * been established, or after a timeout period, if that never 1516 * happens. Because WifiService.updateWifiState() can get called 1517 * multiple times, we can end up acquiring the wake lock and calling 1518 * disconnectAndStop() even when a disconnect or stop operation 1519 * is already in progress. In that case, we want to ignore the 1520 * disconnectAndStop request and release the (ref-counted) wake 1521 * lock, so that eventually, when the mobile data connection is 1522 * established, the ref count will drop to zero. 1523 */ 1524 releaseWakeLock(); 1525 } 1526 return true; 1527 } 1528 1529 public synchronized boolean restart() { 1530 if (mRunState == RUN_STATE_STOPPED) { 1531 mRunState = RUN_STATE_STARTING; 1532 resetInterface(true); 1533 return WifiNative.startDriverCommand(); 1534 } else if (mRunState == RUN_STATE_STOPPING) { 1535 mRunState = RUN_STATE_STARTING; 1536 } 1537 return true; 1538 } 1539 1540 public synchronized boolean removeNetwork(int networkId) { 1541 return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId); 1542 } 1543 1544 public boolean setRadio(boolean turnOn) { 1545 return mWM.setWifiEnabled(turnOn); 1546 } 1547 1548 /** 1549 * {@inheritDoc} 1550 * There are currently no Wi-Fi-specific features supported. 1551 * @param feature the name of the feature 1552 * @return {@code -1} indicating failure, always 1553 */ 1554 public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { 1555 return -1; 1556 } 1557 1558 /** 1559 * {@inheritDoc} 1560 * There are currently no Wi-Fi-specific features supported. 1561 * @param feature the name of the feature 1562 * @return {@code -1} indicating failure, always 1563 */ 1564 public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { 1565 return -1; 1566 } 1567 1568 @Override 1569 public void interpretScanResultsAvailable() { 1570 1571 // If we shouldn't place a notification on available networks, then 1572 // don't bother doing any of the following 1573 if (!mNotificationEnabled) return; 1574 1575 NetworkInfo networkInfo = getNetworkInfo(); 1576 1577 State state = networkInfo.getState(); 1578 if ((state == NetworkInfo.State.DISCONNECTED) 1579 || (state == NetworkInfo.State.UNKNOWN)) { 1580 1581 // Look for an open network 1582 List<ScanResult> scanResults = getScanResultsList(); 1583 if (scanResults != null) { 1584 int numOpenNetworks = 0; 1585 for (int i = scanResults.size() - 1; i >= 0; i--) { 1586 ScanResult scanResult = scanResults.get(i); 1587 1588 if (TextUtils.isEmpty(scanResult.capabilities)) { 1589 numOpenNetworks++; 1590 } 1591 } 1592 1593 if (numOpenNetworks > 0) { 1594 if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) { 1595 /* 1596 * We've scanned continuously at least 1597 * NUM_SCANS_BEFORE_NOTIFICATION times. The user 1598 * probably does not have a remembered network in range, 1599 * since otherwise supplicant would have tried to 1600 * associate and thus resetting this counter. 1601 */ 1602 setNotificationVisible(true, numOpenNetworks, false, 0); 1603 } 1604 return; 1605 } 1606 } 1607 } 1608 1609 // No open networks in range, remove the notification 1610 setNotificationVisible(false, 0, false, 0); 1611 } 1612 1613 /** 1614 * Display or don't display a notification that there are open Wi-Fi networks. 1615 * @param visible {@code true} if notification should be visible, {@code false} otherwise 1616 * @param numNetworks the number networks seen 1617 * @param force {@code true} to force notification to be shown/not-shown, 1618 * even if it is already shown/not-shown. 1619 * @param delay time in milliseconds after which the notification should be made 1620 * visible or invisible. 1621 */ 1622 public void setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay) { 1623 1624 // Since we use auto cancel on the notification, when the 1625 // mNetworksAvailableNotificationShown is true, the notification may 1626 // have actually been canceled. However, when it is false we know 1627 // for sure that it is not being shown (it will not be shown any other 1628 // place than here) 1629 1630 // If it should be hidden and it is already hidden, then noop 1631 if (!visible && !mNotificationShown && !force) { 1632 return; 1633 } 1634 1635 Message message; 1636 if (visible) { 1637 1638 // Not enough time has passed to show the notification again 1639 if (System.currentTimeMillis() < mNotificationRepeatTime) { 1640 return; 1641 } 1642 1643 if (mNotification == null) { 1644 // Cache the Notification mainly so we can remove the 1645 // EVENT_NOTIFICATION_CHANGED message with this Notification from 1646 // the queue later 1647 mNotification = new Notification(); 1648 mNotification.when = 0; 1649 mNotification.icon = ICON_NETWORKS_AVAILABLE; 1650 mNotification.flags = Notification.FLAG_AUTO_CANCEL; 1651 mNotification.contentIntent = PendingIntent.getActivity(mContext, 0, 1652 new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0); 1653 } 1654 1655 CharSequence title = mContext.getResources().getQuantityText( 1656 com.android.internal.R.plurals.wifi_available, numNetworks); 1657 CharSequence details = mContext.getResources().getQuantityText( 1658 com.android.internal.R.plurals.wifi_available_detailed, numNetworks); 1659 mNotification.tickerText = title; 1660 mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent); 1661 1662 mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS; 1663 1664 message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1, 1665 ICON_NETWORKS_AVAILABLE, mNotification); 1666 1667 } else { 1668 1669 // Remove any pending messages to show the notification 1670 mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification); 1671 1672 message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE); 1673 } 1674 1675 mTarget.sendMessageDelayed(message, delay); 1676 1677 mNotificationShown = visible; 1678 } 1679 1680 /** 1681 * Clears variables related to tracking whether a notification has been 1682 * shown recently. 1683 * <p> 1684 * After calling this method, the timer that prevents notifications from 1685 * being shown too often will be cleared. 1686 */ 1687 private void resetNotificationTimer() { 1688 mNotificationRepeatTime = 0; 1689 mNumScansSinceNetworkStateChange = 0; 1690 } 1691 1692 public synchronized boolean addToBlacklist(String bssid) { 1693 return WifiNative.addToBlacklistCommand(bssid); 1694 } 1695 1696 public synchronized boolean clearBlacklist() { 1697 return WifiNative.clearBlacklistCommand(); 1698 } 1699 1700 @Override 1701 public String toString() { 1702 StringBuffer sb = new StringBuffer(); 1703 sb.append("interface ").append(mInterfaceName); 1704 sb.append(" runState="); 1705 if (mRunState >= 1 && mRunState <= mRunStateNames.length) { 1706 sb.append(mRunStateNames[mRunState-1]); 1707 } else { 1708 sb.append(mRunState); 1709 } 1710 sb.append(LS).append(mWifiInfo).append(LS); 1711 sb.append(mDhcpInfo).append(LS); 1712 sb.append("haveIpAddress=").append(mHaveIpAddress). 1713 append(", obtainingIpAddress=").append(mObtainingIpAddress). 1714 append(", scanModeActive=").append(mIsScanModeActive).append(LS). 1715 append("lastSignalLevel=").append(mLastSignalLevel). 1716 append(", explicitlyDisabled=").append(mTornDownByConnMgr); 1717 return sb.toString(); 1718 } 1719 1720 private class DhcpHandler extends Handler { 1721 1722 private Handler mTarget; 1723 1724 /** 1725 * Whether to skip the DHCP result callback to the target. For example, 1726 * this could be set if the network we were requesting an IP for has 1727 * since been disconnected. 1728 * <p> 1729 * Note: There is still a chance where the client's intended DHCP 1730 * request not being canceled. For example, we are request for IP on 1731 * A, and he queues request for IP on B, and then cancels the request on 1732 * B while we're still requesting from A. 1733 */ 1734 private boolean mCancelCallback; 1735 1736 /** 1737 * Instance of the bluetooth headset helper. This needs to be created 1738 * early because there is a delay before it actually 'connects', as 1739 * noted by its javadoc. If we check before it is connected, it will be 1740 * in an error state and we will not disable coexistence. 1741 */ 1742 private BluetoothHeadset mBluetoothHeadset; 1743 1744 public DhcpHandler(Looper looper, Handler target) { 1745 super(looper); 1746 mTarget = target; 1747 1748 mBluetoothHeadset = new BluetoothHeadset(mContext, null); 1749 } 1750 1751 public void handleMessage(Message msg) { 1752 int event; 1753 1754 switch (msg.what) { 1755 case EVENT_DHCP_START: 1756 1757 boolean modifiedBluetoothCoexistenceMode = false; 1758 int powerMode = DRIVER_POWER_MODE_AUTO; 1759 1760 if (shouldDisableCoexistenceMode()) { 1761 /* 1762 * There are problems setting the Wi-Fi driver's power 1763 * mode to active when bluetooth coexistence mode is 1764 * enabled or sense. 1765 * <p> 1766 * We set Wi-Fi to active mode when 1767 * obtaining an IP address because we've found 1768 * compatibility issues with some routers with low power 1769 * mode. 1770 * <p> 1771 * In order for this active power mode to properly be set, 1772 * we disable coexistence mode until we're done with 1773 * obtaining an IP address. One exception is if we 1774 * are currently connected to a headset, since disabling 1775 * coexistence would interrupt that connection. 1776 */ 1777 modifiedBluetoothCoexistenceMode = true; 1778 1779 // Disable the coexistence mode 1780 synchronized (WifiStateTracker.this) { 1781 WifiNative.setBluetoothCoexistenceModeCommand( 1782 WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); 1783 } 1784 } 1785 1786 synchronized (WifiStateTracker.this) { 1787 powerMode = WifiNative.getPowerModeCommand(); 1788 if (powerMode < 0) { 1789 // Handle the case where supplicant driver does not support 1790 // getPowerModeCommand. 1791 powerMode = DRIVER_POWER_MODE_AUTO; 1792 } 1793 if (powerMode != DRIVER_POWER_MODE_ACTIVE) { 1794 WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE); 1795 } 1796 } 1797 synchronized (this) { 1798 // A new request is being made, so assume we will callback 1799 mCancelCallback = false; 1800 } 1801 Log.d(TAG, "DhcpHandler: DHCP request started"); 1802 if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) { 1803 event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED; 1804 if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded"); 1805 } else { 1806 event = EVENT_INTERFACE_CONFIGURATION_FAILED; 1807 Log.i(TAG, "DhcpHandler: DHCP request failed: " + 1808 NetworkUtils.getDhcpError()); 1809 } 1810 synchronized (WifiStateTracker.this) { 1811 if (powerMode != DRIVER_POWER_MODE_ACTIVE) { 1812 WifiNative.setPowerModeCommand(powerMode); 1813 } 1814 } 1815 1816 if (modifiedBluetoothCoexistenceMode) { 1817 // Set the coexistence mode back to its default value 1818 synchronized (WifiStateTracker.this) { 1819 WifiNative.setBluetoothCoexistenceModeCommand( 1820 WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); 1821 } 1822 } 1823 1824 synchronized (this) { 1825 if (!mCancelCallback) { 1826 mTarget.sendEmptyMessage(event); 1827 } 1828 } 1829 break; 1830 } 1831 } 1832 1833 public synchronized void setCancelCallback(boolean cancelCallback) { 1834 mCancelCallback = cancelCallback; 1835 } 1836 1837 /** 1838 * Whether to disable coexistence mode while obtaining IP address. This 1839 * logic will return true only if the current bluetooth 1840 * headset/handsfree state is disconnected. This means if it is in an 1841 * error state, we will NOT disable coexistence mode to err on the side 1842 * of safety. 1843 * 1844 * @return Whether to disable coexistence mode. 1845 */ 1846 private boolean shouldDisableCoexistenceMode() { 1847 int state = mBluetoothHeadset.getState(); 1848 return state == BluetoothHeadset.STATE_DISCONNECTED; 1849 } 1850 } 1851 1852 private void checkUseStaticIp() { 1853 mUseStaticIp = false; 1854 final ContentResolver cr = mContext.getContentResolver(); 1855 try { 1856 if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) { 1857 return; 1858 } 1859 } catch (Settings.SettingNotFoundException e) { 1860 return; 1861 } 1862 1863 try { 1864 String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP); 1865 if (addr != null) { 1866 mDhcpInfo.ipAddress = stringToIpAddr(addr); 1867 } else { 1868 return; 1869 } 1870 addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY); 1871 if (addr != null) { 1872 mDhcpInfo.gateway = stringToIpAddr(addr); 1873 } else { 1874 return; 1875 } 1876 addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK); 1877 if (addr != null) { 1878 mDhcpInfo.netmask = stringToIpAddr(addr); 1879 } else { 1880 return; 1881 } 1882 addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1); 1883 if (addr != null) { 1884 mDhcpInfo.dns1 = stringToIpAddr(addr); 1885 } else { 1886 return; 1887 } 1888 addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2); 1889 if (addr != null) { 1890 mDhcpInfo.dns2 = stringToIpAddr(addr); 1891 } else { 1892 mDhcpInfo.dns2 = 0; 1893 } 1894 } catch (UnknownHostException e) { 1895 return; 1896 } 1897 mUseStaticIp = true; 1898 } 1899 1900 private static int stringToIpAddr(String addrString) throws UnknownHostException { 1901 try { 1902 String[] parts = addrString.split("\\."); 1903 if (parts.length != 4) { 1904 throw new UnknownHostException(addrString); 1905 } 1906 1907 int a = Integer.parseInt(parts[0]) ; 1908 int b = Integer.parseInt(parts[1]) << 8; 1909 int c = Integer.parseInt(parts[2]) << 16; 1910 int d = Integer.parseInt(parts[3]) << 24; 1911 1912 return a | b | c | d; 1913 } catch (NumberFormatException ex) { 1914 throw new UnknownHostException(addrString); 1915 } 1916 } 1917 1918 private int getMaxDhcpRetries() { 1919 return Settings.Secure.getInt(mContext.getContentResolver(), 1920 Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT, 1921 DEFAULT_MAX_DHCP_RETRIES); 1922 } 1923 1924 private class SettingsObserver extends ContentObserver { 1925 public SettingsObserver(Handler handler) { 1926 super(handler); 1927 ContentResolver cr = mContext.getContentResolver(); 1928 cr.registerContentObserver(Settings.System.getUriFor( 1929 Settings.System.WIFI_USE_STATIC_IP), false, this); 1930 cr.registerContentObserver(Settings.System.getUriFor( 1931 Settings.System.WIFI_STATIC_IP), false, this); 1932 cr.registerContentObserver(Settings.System.getUriFor( 1933 Settings.System.WIFI_STATIC_GATEWAY), false, this); 1934 cr.registerContentObserver(Settings.System.getUriFor( 1935 Settings.System.WIFI_STATIC_NETMASK), false, this); 1936 cr.registerContentObserver(Settings.System.getUriFor( 1937 Settings.System.WIFI_STATIC_DNS1), false, this); 1938 cr.registerContentObserver(Settings.System.getUriFor( 1939 Settings.System.WIFI_STATIC_DNS2), false, this); 1940 } 1941 1942 public void onChange(boolean selfChange) { 1943 super.onChange(selfChange); 1944 1945 boolean wasStaticIp = mUseStaticIp; 1946 int oIp, oGw, oMsk, oDns1, oDns2; 1947 oIp = oGw = oMsk = oDns1 = oDns2 = 0; 1948 if (wasStaticIp) { 1949 oIp = mDhcpInfo.ipAddress; 1950 oGw = mDhcpInfo.gateway; 1951 oMsk = mDhcpInfo.netmask; 1952 oDns1 = mDhcpInfo.dns1; 1953 oDns2 = mDhcpInfo.dns2; 1954 } 1955 checkUseStaticIp(); 1956 1957 if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) { 1958 return; 1959 } 1960 1961 boolean changed = 1962 (wasStaticIp != mUseStaticIp) || 1963 (wasStaticIp && ( 1964 oIp != mDhcpInfo.ipAddress || 1965 oGw != mDhcpInfo.gateway || 1966 oMsk != mDhcpInfo.netmask || 1967 oDns1 != mDhcpInfo.dns1 || 1968 oDns2 != mDhcpInfo.dns2)); 1969 1970 if (changed) { 1971 resetInterface(true); 1972 configureInterface(); 1973 if (mUseStaticIp) { 1974 mTarget.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED); 1975 } 1976 } 1977 } 1978 } 1979 1980 private class NotificationEnabledSettingObserver extends ContentObserver { 1981 1982 public NotificationEnabledSettingObserver(Handler handler) { 1983 super(handler); 1984 } 1985 1986 public void register() { 1987 ContentResolver cr = mContext.getContentResolver(); 1988 cr.registerContentObserver(Settings.Secure.getUriFor( 1989 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this); 1990 mNotificationEnabled = getValue(); 1991 } 1992 1993 @Override 1994 public void onChange(boolean selfChange) { 1995 super.onChange(selfChange); 1996 1997 mNotificationEnabled = getValue(); 1998 if (!mNotificationEnabled) { 1999 // Remove any notification that may be showing 2000 setNotificationVisible(false, 0, true, 0); 2001 } 2002 2003 resetNotificationTimer(); 2004 } 2005 2006 private boolean getValue() { 2007 return Settings.Secure.getInt(mContext.getContentResolver(), 2008 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; 2009 } 2010 } 2011} 2012