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