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