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