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