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