WifiStateMachine.java revision 8b2bd657976c86e0909ad792f6f96ad94db45c28
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wifi; 18 19import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; 20import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; 21import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; 22import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; 23import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; 24 25/** 26 * TODO: 27 * Deprecate WIFI_STATE_UNKNOWN 28 */ 29import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; 30import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; 31import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; 32import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; 33import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; 34 35import android.app.AlarmManager; 36import android.app.PendingIntent; 37import android.app.backup.IBackupManager; 38import android.bluetooth.BluetoothAdapter; 39import android.content.BroadcastReceiver; 40import android.content.Context; 41import android.content.Intent; 42import android.content.IntentFilter; 43import android.content.pm.PackageManager; 44import android.database.ContentObserver; 45import android.net.*; 46import android.net.NetworkInfo.DetailedState; 47import android.net.wifi.BatchedScanResult; 48import android.net.wifi.BatchedScanSettings; 49import android.net.wifi.RssiPacketCountInfo; 50import android.net.wifi.ScanResult; 51import android.net.wifi.ScanSettings; 52import android.net.wifi.WifiChannel; 53import android.net.wifi.SupplicantState; 54import android.net.wifi.WifiConfiguration; 55import android.net.wifi.WifiInfo; 56import android.net.wifi.WifiLinkLayerStats; 57import android.net.wifi.WifiManager; 58import android.net.wifi.WifiSsid; 59import android.net.wifi.WpsInfo; 60import android.net.wifi.WpsResult; 61import android.net.wifi.WpsResult.Status; 62import android.net.wifi.p2p.IWifiP2pManager; 63import android.net.wifi.passpoint.IWifiPasspointManager; 64import android.os.*; 65import android.os.Process; 66import android.provider.Settings; 67import android.util.LruCache; 68import android.text.TextUtils; 69import android.util.Log; 70 71import com.android.internal.R; 72import com.android.internal.app.IBatteryStats; 73import com.android.internal.util.AsyncChannel; 74import com.android.internal.util.Protocol; 75import com.android.internal.util.State; 76import com.android.internal.util.StateMachine; 77import com.android.server.net.BaseNetworkObserver; 78import com.android.server.net.NetlinkTracker; 79 80import com.android.server.wifi.p2p.WifiP2pServiceImpl; 81import com.android.server.wifi.passpoint.WifiPasspointServiceImpl; 82import com.android.server.wifi.passpoint.WifiPasspointStateMachine; 83 84import java.io.FileDescriptor; 85import java.io.PrintWriter; 86import java.net.InetAddress; 87import java.util.*; 88import java.util.concurrent.atomic.AtomicInteger; 89import java.util.concurrent.atomic.AtomicBoolean; 90import java.util.regex.Pattern; 91import java.io.FileReader; 92import java.io.BufferedReader; 93import java.io.FileNotFoundException; 94import java.io.IOException; 95import java.net.InetAddress; 96import java.net.Inet4Address; 97import java.net.Inet6Address; 98 99/** 100 * Track the state of Wifi connectivity. All event handling is done here, 101 * and all changes in connectivity state are initiated here. 102 * 103 * Wi-Fi now supports three modes of operation: Client, SoftAp and p2p 104 * In the current implementation, we support concurrent wifi p2p and wifi operation. 105 * The WifiStateMachine handles SoftAp and Client operations while WifiP2pService 106 * handles p2p operation. 107 * 108 * @hide 109 */ 110public class WifiStateMachine extends StateMachine { 111 112 private static final String NETWORKTYPE = "WIFI"; 113 private static boolean DBG = false; 114 private static boolean VDBG = false; 115 private static boolean mLogMessages = false; 116 117 /* temporary debug flag - best network selection development */ 118 private static boolean PDBG = false; 119 /** 120 * Log with error attribute 121 * 122 * @param s is string log 123 */ 124 protected void loge(String s) { 125 Log.e(getName(), s); 126 } 127 protected void log(String s) {; 128 Log.e(getName(), s); 129 } 130 131 private WifiMonitor mWifiMonitor; 132 private WifiNative mWifiNative; 133 private WifiConfigStore mWifiConfigStore; 134 private WifiAutoJoinController mWifiAutoJoinController; 135 private INetworkManagementService mNwService; 136 private ConnectivityManager mCm; 137 138 private final boolean mP2pSupported; 139 private final AtomicBoolean mP2pConnected = new AtomicBoolean(false); 140 private boolean mTemporarilyDisconnectWifi = false; 141 private final String mPrimaryDeviceType; 142 143 /* Scan results handling */ 144 private List<ScanResult> mScanResults = new ArrayList<ScanResult>(); 145 private static final Pattern scanResultPattern = Pattern.compile("\t+"); 146 private static final int SCAN_RESULT_CACHE_SIZE = 80; 147 private final LruCache<String, ScanResult> mScanResultCache; 148 149 /* Batch scan results */ 150 private final List<BatchedScanResult> mBatchedScanResults = 151 new ArrayList<BatchedScanResult>(); 152 private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE; 153 private int mExpectedBatchedScans = 0; 154 private long mBatchedScanMinPollTime = 0; 155 156 private boolean mScreenOn = false; 157 158 /* Chipset supports background scan */ 159 private final boolean mBackgroundScanSupported; 160 161 private String mInterfaceName; 162 /* Tethering interface could be separate from wlan interface */ 163 private String mTetherInterfaceName; 164 165 private int mLastSignalLevel = -1; 166 private String mLastBssid; 167 private int mLastNetworkId; //The network Id we successfully joined 168 private boolean mEnableRssiPolling = false; 169 private boolean mEnableBackgroundScan = false; 170 private int mRssiPollToken = 0; 171 private int mReconnectCount = 0; 172 /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE 173 * In CONNECT_MODE, the STA can scan and connect to an access point 174 * In SCAN_ONLY_MODE, the STA can only scan for access points 175 * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off 176 */ 177 private int mOperationalMode = CONNECT_MODE; 178 private boolean mIsScanOngoing = false; 179 private boolean mIsFullScanOngoing = false; 180 private final Queue<Message> mBufferedScanMsg = new LinkedList<Message>(); 181 private WorkSource mScanWorkSource = null; 182 private static final int UNKNOWN_SCAN_SOURCE = -1; 183 private static final int SCAN_ALARM_SOURCE = -2; 184 185 private static final int SCAN_REQUEST_BUFFER_MAX_SIZE = 10; 186 private static final String CUSTOMIZED_SCAN_SETTING = "customized_scan_settings"; 187 private static final String CUSTOMIZED_SCAN_WORKSOURCE = "customized_scan_worksource"; 188 private static final String BATCHED_SETTING = "batched_settings"; 189 private static final String BATCHED_WORKSOURCE = "batched_worksource"; 190 191 /* Tracks if state machine has received any screen state change broadcast yet. 192 * We can miss one of these at boot. 193 */ 194 private AtomicBoolean mScreenBroadcastReceived = new AtomicBoolean(false); 195 196 private boolean mBluetoothConnectionActive = false; 197 198 private PowerManager.WakeLock mSuspendWakeLock; 199 200 /** 201 * Interval in milliseconds between polling for RSSI 202 * and linkspeed information 203 */ 204 private static final int POLL_RSSI_INTERVAL_MSECS = 3000; 205 206 /** 207 * Delay between supplicant restarts upon failure to establish connection 208 */ 209 private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000; 210 211 /** 212 * Number of times we attempt to restart supplicant 213 */ 214 private static final int SUPPLICANT_RESTART_TRIES = 5; 215 216 private int mSupplicantRestartCount = 0; 217 /* Tracks sequence number on stop failure message */ 218 private int mSupplicantStopFailureToken = 0; 219 220 /** 221 * Tether state change notification time out 222 */ 223 private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000; 224 225 /* Tracks sequence number on a tether notification time out */ 226 private int mTetherToken = 0; 227 228 /** 229 * Driver start time out. 230 */ 231 private static final int DRIVER_START_TIME_OUT_MSECS = 10000; 232 233 /* Tracks sequence number on a driver time out */ 234 private int mDriverStartToken = 0; 235 236 /** 237 * The link properties of the wifi interface. 238 * Do not modify this directly; use updateLinkProperties instead. 239 */ 240 private LinkProperties mLinkProperties; 241 242 /* Tracks sequence number on a periodic scan message */ 243 private int mPeriodicScanToken = 0; 244 245 // Wakelock held during wifi start/stop and driver load/unload 246 private PowerManager.WakeLock mWakeLock; 247 248 private Context mContext; 249 250 private final Object mDhcpResultsLock = new Object(); 251 private DhcpResults mDhcpResults; 252 private WifiInfo mWifiInfo; 253 private NetworkInfo mNetworkInfo; 254 private NetworkCapabilities mNetworkCapabilities; 255 private SupplicantStateTracker mSupplicantStateTracker; 256 private DhcpStateMachine mDhcpStateMachine; 257 private boolean mDhcpActive = false; 258 259 private int mWifiLinkLayerStatsSupported = 4; 260 261 private final AtomicInteger mCountryCodeSequence = new AtomicInteger(); 262 263 //whether the state machine goes thru the Disconnecting->Disconnected->ObtainingIpAddress 264 private int mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE; 265 266 private String mTargetRoamBSSID = "any"; 267 268 private WifiConfiguration targetWificonfiguration = null; 269 270 boolean isRoaming() { 271 return mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_ROAMING 272 || mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_EXTENDED_ROAMING; 273 } 274 275 public void autoRoamSetBSSID(int netId, String bssid) { 276 autoRoamSetBSSID(mWifiConfigStore.getWifiConfiguration(netId), bssid); 277 } 278 279 public void autoRoamSetBSSID(WifiConfiguration config, String bssid) { 280 mTargetRoamBSSID = "any"; 281 if (config == null) 282 return; 283 if (config.bssidOwnerUid == 0 || config.bssidOwnerUid == Process.WIFI_UID) { 284 if (VDBG) { 285 loge("autoRoamSetBSSID uid=" + Long.toString(config.bssidOwnerUid) 286 + " bssid=" + bssid 287 + " key=" + config.configKey()); 288 } 289 config.bssidOwnerUid = Process.WIFI_UID; 290 config.BSSID = bssid; 291 mTargetRoamBSSID = bssid; 292 } 293 } 294 295 static private final int ONE_HOUR_MILLI = 1000 * 60 * 60; 296 297 /** 298 * Subset of link properties coming from netlink. 299 * Currently includes IPv4 and IPv6 addresses. In the future will also include IPv6 DNS servers 300 * and domains obtained from router advertisements (RFC 6106). 301 */ 302 private NetlinkTracker mNetlinkTracker; 303 304 private AlarmManager mAlarmManager; 305 private PendingIntent mScanIntent; 306 private PendingIntent mDriverStopIntent; 307 private PendingIntent mBatchedScanIntervalIntent; 308 309 /* Tracks current frequency mode */ 310 private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO); 311 312 /* Tracks if we are filtering Multicast v4 packets. Default is to filter. */ 313 private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true); 314 315 // Channel for sending replies. 316 private AsyncChannel mReplyChannel = new AsyncChannel(); 317 318 private WifiP2pServiceImpl mWifiP2pServiceImpl; 319 private WifiPasspointServiceImpl mPasspointServiceImpl; 320 321 // Used to initiate a connection with WifiP2pService 322 private AsyncChannel mWifiP2pChannel; 323 private AsyncChannel mWifiApConfigChannel; 324 325 private WifiNetworkFactory mNetworkFactory; 326 private WifiNetworkAgent mNetworkAgent; 327 328 // Used to filter out requests we couldn't possibly satisfy. 329 private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities(); 330 331 /* The base for wifi message types */ 332 static final int BASE = Protocol.BASE_WIFI; 333 /* Start the supplicant */ 334 static final int CMD_START_SUPPLICANT = BASE + 11; 335 /* Stop the supplicant */ 336 static final int CMD_STOP_SUPPLICANT = BASE + 12; 337 /* Start the driver */ 338 static final int CMD_START_DRIVER = BASE + 13; 339 /* Stop the driver */ 340 static final int CMD_STOP_DRIVER = BASE + 14; 341 /* Indicates Static IP succeeded */ 342 static final int CMD_STATIC_IP_SUCCESS = BASE + 15; 343 /* Indicates Static IP failed */ 344 static final int CMD_STATIC_IP_FAILURE = BASE + 16; 345 /* Indicates supplicant stop failed */ 346 static final int CMD_STOP_SUPPLICANT_FAILED = BASE + 17; 347 /* Delayed stop to avoid shutting down driver too quick*/ 348 static final int CMD_DELAYED_STOP_DRIVER = BASE + 18; 349 /* A delayed message sent to start driver when it fail to come up */ 350 static final int CMD_DRIVER_START_TIMED_OUT = BASE + 19; 351 352 /* Start the soft access point */ 353 static final int CMD_START_AP = BASE + 21; 354 /* Indicates soft ap start succeeded */ 355 static final int CMD_START_AP_SUCCESS = BASE + 22; 356 /* Indicates soft ap start failed */ 357 static final int CMD_START_AP_FAILURE = BASE + 23; 358 /* Stop the soft access point */ 359 static final int CMD_STOP_AP = BASE + 24; 360 /* Set the soft access point configuration */ 361 static final int CMD_SET_AP_CONFIG = BASE + 25; 362 /* Soft access point configuration set completed */ 363 static final int CMD_SET_AP_CONFIG_COMPLETED = BASE + 26; 364 /* Request the soft access point configuration */ 365 static final int CMD_REQUEST_AP_CONFIG = BASE + 27; 366 /* Response to access point configuration request */ 367 static final int CMD_RESPONSE_AP_CONFIG = BASE + 28; 368 /* Invoked when getting a tether state change notification */ 369 static final int CMD_TETHER_STATE_CHANGE = BASE + 29; 370 /* A delayed message sent to indicate tether state change failed to arrive */ 371 static final int CMD_TETHER_NOTIFICATION_TIMED_OUT = BASE + 30; 372 373 static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31; 374 375 /* Supplicant commands */ 376 /* Is supplicant alive ? */ 377 static final int CMD_PING_SUPPLICANT = BASE + 51; 378 /* Add/update a network configuration */ 379 static final int CMD_ADD_OR_UPDATE_NETWORK = BASE + 52; 380 /* Delete a network */ 381 static final int CMD_REMOVE_NETWORK = BASE + 53; 382 /* Enable a network. The device will attempt a connection to the given network. */ 383 static final int CMD_ENABLE_NETWORK = BASE + 54; 384 /* Enable all networks */ 385 static final int CMD_ENABLE_ALL_NETWORKS = BASE + 55; 386 /* Blacklist network. De-prioritizes the given BSSID for connection. */ 387 static final int CMD_BLACKLIST_NETWORK = BASE + 56; 388 /* Clear the blacklist network list */ 389 static final int CMD_CLEAR_BLACKLIST = BASE + 57; 390 /* Save configuration */ 391 static final int CMD_SAVE_CONFIG = BASE + 58; 392 /* Get configured networks */ 393 static final int CMD_GET_CONFIGURED_NETWORKS = BASE + 59; 394 /* Get available frequencies */ 395 static final int CMD_GET_CAPABILITY_FREQ = BASE + 60; 396 397 /* Supplicant commands after driver start*/ 398 /* Initiate a scan */ 399 static final int CMD_START_SCAN = BASE + 71; 400 /* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */ 401 static final int CMD_SET_OPERATIONAL_MODE = BASE + 72; 402 /* Disconnect from a network */ 403 static final int CMD_DISCONNECT = BASE + 73; 404 /* Reconnect to a network */ 405 static final int CMD_RECONNECT = BASE + 74; 406 /* Reassociate to a network */ 407 static final int CMD_REASSOCIATE = BASE + 75; 408 /* Controls suspend mode optimizations 409 * 410 * When high perf mode is enabled, suspend mode optimizations are disabled 411 * 412 * When high perf mode is disabled, suspend mode optimizations are enabled 413 * 414 * Suspend mode optimizations include: 415 * - packet filtering 416 * - turn off roaming 417 * - DTIM wake up settings 418 */ 419 static final int CMD_SET_HIGH_PERF_MODE = BASE + 77; 420 /* Set the country code */ 421 static final int CMD_SET_COUNTRY_CODE = BASE + 80; 422 /* Enables RSSI poll */ 423 static final int CMD_ENABLE_RSSI_POLL = BASE + 82; 424 /* RSSI poll */ 425 static final int CMD_RSSI_POLL = BASE + 83; 426 /* Set up packet filtering */ 427 static final int CMD_START_PACKET_FILTERING = BASE + 84; 428 /* Clear packet filter */ 429 static final int CMD_STOP_PACKET_FILTERING = BASE + 85; 430 /* Enable suspend mode optimizations in the driver */ 431 static final int CMD_SET_SUSPEND_OPT_ENABLED = BASE + 86; 432 /* When there are no saved networks, we do a periodic scan to notify user of 433 * an open network */ 434 static final int CMD_NO_NETWORKS_PERIODIC_SCAN = BASE + 88; 435 436 /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */ 437 static final int MULTICAST_V6 = 1; 438 static final int MULTICAST_V4 = 0; 439 440 /* Set the frequency band */ 441 static final int CMD_SET_FREQUENCY_BAND = BASE + 90; 442 /* Enable background scan for configured networks */ 443 static final int CMD_ENABLE_BACKGROUND_SCAN = BASE + 91; 444 /* Enable TDLS on a specific MAC address */ 445 static final int CMD_ENABLE_TDLS = BASE + 92; 446 447 /* Commands from/to the SupplicantStateTracker */ 448 /* Reset the supplicant state tracker */ 449 static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111; 450 451 /* P2p commands */ 452 /* We are ok with no response here since we wont do much with it anyway */ 453 public static final int CMD_ENABLE_P2P = BASE + 131; 454 /* In order to shut down supplicant cleanly, we wait till p2p has 455 * been disabled */ 456 public static final int CMD_DISABLE_P2P_REQ = BASE + 132; 457 public static final int CMD_DISABLE_P2P_RSP = BASE + 133; 458 459 public static final int CMD_BOOT_COMPLETED = BASE + 134; 460 461 /* change the batch scan settings. 462 * arg1 = responsible UID 463 * arg2 = csph (channel scans per hour) 464 * obj = bundle with the new settings and the optional worksource 465 */ 466 public static final int CMD_SET_BATCHED_SCAN = BASE + 135; 467 public static final int CMD_START_NEXT_BATCHED_SCAN = BASE + 136; 468 public static final int CMD_POLL_BATCHED_SCAN = BASE + 137; 469 470 /* We now have a valid IP configuration. */ 471 static final int CMD_IP_CONFIGURATION_SUCCESSFUL = BASE + 138; 472 /* We no longer have a valid IP configuration. */ 473 static final int CMD_IP_CONFIGURATION_LOST = BASE + 139; 474 /* Link configuration (IP address, DNS, ...) changes notified via netlink */ 475 static final int CMD_NETLINK_UPDATE = BASE + 140; 476 477 478 /* Reload all networks and reconnect */ 479 static final int CMD_RELOAD_TLS_AND_RECONNECT = BASE + 142; 480 481 static final int CMD_AUTO_CONNECT = BASE + 143; 482 483 static final int CMD_UNWANTED_NETWORK = BASE + 144; 484 485 static final int CMD_AUTO_ROAM = BASE + 145; 486 487 488 /* Wifi state machine modes of operation */ 489 /* CONNECT_MODE - connect to any 'known' AP when it becomes available */ 490 public static final int CONNECT_MODE = 1; 491 /* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */ 492 public static final int SCAN_ONLY_MODE = 2; 493 /* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */ 494 public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3; 495 496 private static final int SUCCESS = 1; 497 private static final int FAILURE = -1; 498 499 /** 500 * The maximum number of times we will retry a connection to an access point 501 * for which we have failed in acquiring an IP address from DHCP. A value of 502 * N means that we will make N+1 connection attempts in all. 503 * <p> 504 * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default 505 * value if a Settings value is not present. 506 */ 507 private static final int DEFAULT_MAX_DHCP_RETRIES = 9; 508 509 /* Tracks if suspend optimizations need to be disabled by DHCP, 510 * screen or due to high perf mode. 511 * When any of them needs to disable it, we keep the suspend optimizations 512 * disabled 513 */ 514 private int mSuspendOptNeedsDisabled = 0; 515 516 private static final int SUSPEND_DUE_TO_DHCP = 1; 517 private static final int SUSPEND_DUE_TO_HIGH_PERF = 1<<1; 518 private static final int SUSPEND_DUE_TO_SCREEN = 1<<2; 519 520 /* Tracks if user has enabled suspend optimizations through settings */ 521 private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true); 522 523 /** 524 * Default framework scan interval in milliseconds. This is used in the scenario in which 525 * wifi chipset does not support background scanning to set up a 526 * periodic wake up scan so that the device can connect to a new access 527 * point on the move. {@link Settings.Global#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can 528 * override this. 529 */ 530 private final int mDefaultFrameworkScanIntervalMs; 531 532 /** 533 * Connected state framework scan interval in milliseconds. 534 * This is used for extended roaming, when screen is lit. 535 */ 536 private int mConnectedScanPeriodMs = 10000; 537 private int mDisconnectedScanPeriodMs = 10000; 538 539 /** 540 * Supplicant scan interval in milliseconds. 541 * Comes from {@link Settings.Global#WIFI_SUPPLICANT_SCAN_INTERVAL_MS} or 542 * from the default config if the setting is not set 543 */ 544 private long mSupplicantScanIntervalMs; 545 546 /** 547 * timeStamp of last full band scan we perfoemed for autojoin while connected with screen lit 548 */ 549 private long lastFullBandConnectedTimeMilli; 550 551 /** 552 * time interval to the next full band scan we will perform for 553 * autojoin while connected with screen lit 554 */ 555 private long fullBandConnectedTimeIntervalMilli; 556 557 /** 558 * max time interval to the next full band scan we will perform for 559 * autojoin while connected with screen lit 560 * Max time is 5 minutes 561 */ 562 private static final long maxFullBandConnectedTimeIntervalMilli = 1000 * 60 * 5; 563 564 /** 565 * Minimum time interval between enabling all networks. 566 * A device can end up repeatedly connecting to a bad network on screen on/off toggle 567 * due to enabling every time. We add a threshold to avoid this. 568 */ 569 private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */ 570 private long mLastEnableAllNetworksTime; 571 572 /** 573 * Starting and shutting down driver too quick causes problems leading to driver 574 * being in a bad state. Delay driver stop. 575 */ 576 private final int mDriverStopDelayMs; 577 private int mDelayedStopCounter; 578 private boolean mInDelayedStop = false; 579 580 // sometimes telephony gives us this data before boot is complete and we can't store it 581 // until after, so the write is deferred 582 private volatile String mPersistedCountryCode; 583 584 // Supplicant doesn't like setting the same country code multiple times (it may drop 585 // currently connected network), so we save the country code here to avoid redundency 586 private String mLastSetCountryCode; 587 588 /* Default parent state */ 589 private State mDefaultState = new DefaultState(); 590 /* Temporary initial state */ 591 private State mInitialState = new InitialState(); 592 /* Driver loaded, waiting for supplicant to start */ 593 private State mSupplicantStartingState = new SupplicantStartingState(); 594 /* Driver loaded and supplicant ready */ 595 private State mSupplicantStartedState = new SupplicantStartedState(); 596 /* Waiting for supplicant to stop and monitor to exit */ 597 private State mSupplicantStoppingState = new SupplicantStoppingState(); 598 /* Driver start issued, waiting for completed event */ 599 private State mDriverStartingState = new DriverStartingState(); 600 /* Driver started */ 601 private State mDriverStartedState = new DriverStartedState(); 602 /* Wait until p2p is disabled 603 * This is a special state which is entered right after we exit out of DriverStartedState 604 * before transitioning to another state. 605 */ 606 private State mWaitForP2pDisableState = new WaitForP2pDisableState(); 607 /* Driver stopping */ 608 private State mDriverStoppingState = new DriverStoppingState(); 609 /* Driver stopped */ 610 private State mDriverStoppedState = new DriverStoppedState(); 611 /* Scan for networks, no connection will be established */ 612 private State mScanModeState = new ScanModeState(); 613 /* Connecting to an access point */ 614 private State mConnectModeState = new ConnectModeState(); 615 /* Connected at 802.11 (L2) level */ 616 private State mL2ConnectedState = new L2ConnectedState(); 617 /* fetching IP after connection to access point (assoc+auth complete) */ 618 private State mObtainingIpState = new ObtainingIpState(); 619 /* Waiting for link quality verification to be complete */ 620 private State mVerifyingLinkState = new VerifyingLinkState(); 621 /* Connected with IP addr */ 622 private State mConnectedState = new ConnectedState(); 623 /* Roaming */ 624 private State mRoamingState = new RoamingState(); 625 /* disconnect issued, waiting for network disconnect confirmation */ 626 private State mDisconnectingState = new DisconnectingState(); 627 /* Network is not connected, supplicant assoc+auth is not complete */ 628 private State mDisconnectedState = new DisconnectedState(); 629 /* Waiting for WPS to be completed*/ 630 private State mWpsRunningState = new WpsRunningState(); 631 632 /* Soft ap is starting up */ 633 private State mSoftApStartingState = new SoftApStartingState(); 634 /* Soft ap is running */ 635 private State mSoftApStartedState = new SoftApStartedState(); 636 /* Soft ap is running and we are waiting for tether notification */ 637 private State mTetheringState = new TetheringState(); 638 /* Soft ap is running and we are tethered through connectivity service */ 639 private State mTetheredState = new TetheredState(); 640 /* Waiting for untether confirmation before stopping soft Ap */ 641 private State mUntetheringState = new UntetheringState(); 642 643 private class TetherStateChange { 644 ArrayList<String> available; 645 ArrayList<String> active; 646 TetherStateChange(ArrayList<String> av, ArrayList<String> ac) { 647 available = av; 648 active = ac; 649 } 650 } 651 652 653 /** 654 * One of {@link WifiManager#WIFI_STATE_DISABLED}, 655 * {@link WifiManager#WIFI_STATE_DISABLING}, 656 * {@link WifiManager#WIFI_STATE_ENABLED}, 657 * {@link WifiManager#WIFI_STATE_ENABLING}, 658 * {@link WifiManager#WIFI_STATE_UNKNOWN} 659 * 660 */ 661 private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED); 662 663 /** 664 * One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, 665 * {@link WifiManager#WIFI_AP_STATE_DISABLING}, 666 * {@link WifiManager#WIFI_AP_STATE_ENABLED}, 667 * {@link WifiManager#WIFI_AP_STATE_ENABLING}, 668 * {@link WifiManager#WIFI_AP_STATE_FAILED} 669 * 670 */ 671 private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED); 672 673 private static final int SCAN_REQUEST = 0; 674 private static final String ACTION_START_SCAN = 675 "com.android.server.WifiManager.action.START_SCAN"; 676 677 private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter"; 678 private static final int DRIVER_STOP_REQUEST = 0; 679 private static final String ACTION_DELAYED_DRIVER_STOP = 680 "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP"; 681 682 private static final String ACTION_REFRESH_BATCHED_SCAN = 683 "com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN"; 684 /** 685 * Keep track of whether WIFI is running. 686 */ 687 private boolean mIsRunning = false; 688 689 /** 690 * Keep track of whether we last told the battery stats we had started. 691 */ 692 private boolean mReportedRunning = false; 693 694 /** 695 * Most recently set source of starting WIFI. 696 */ 697 private final WorkSource mRunningWifiUids = new WorkSource(); 698 699 /** 700 * The last reported UIDs that were responsible for starting WIFI. 701 */ 702 private final WorkSource mLastRunningWifiUids = new WorkSource(); 703 704 private final IBatteryStats mBatteryStats; 705 706 private BatchedScanSettings mBatchedScanSettings = null; 707 708 /** 709 * Track the worksource/cost of the current settings and track what's been noted 710 * to the battery stats, so we can mark the end of the previous when changing. 711 */ 712 private WorkSource mBatchedScanWorkSource = null; 713 private int mBatchedScanCsph = 0; 714 private WorkSource mNotedBatchedScanWorkSource = null; 715 private int mNotedBatchedScanCsph = 0; 716 717 private AtomicBoolean mFrameworkAutoJoin = new AtomicBoolean(true); //enable by default 718 719 public WifiStateMachine(Context context, String wlanInterface, WifiTrafficPoller trafficPoller){ 720 super("WifiStateMachine"); 721 mContext = context; 722 mInterfaceName = wlanInterface; 723 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, ""); 724 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( 725 BatteryStats.SERVICE_NAME)); 726 727 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 728 mNwService = INetworkManagementService.Stub.asInterface(b); 729 730 mP2pSupported = mContext.getPackageManager().hasSystemFeature( 731 PackageManager.FEATURE_WIFI_DIRECT); 732 733 mWifiNative = new WifiNative(mInterfaceName); 734 mWifiConfigStore = new WifiConfigStore(context, mWifiNative); 735 mWifiAutoJoinController = new WifiAutoJoinController(context, this, 736 mWifiConfigStore, trafficPoller, mWifiNative); 737 mWifiMonitor = new WifiMonitor(this, mWifiNative); 738 mWifiInfo = new WifiInfo(); 739 mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore, 740 getHandler()); 741 mLinkProperties = new LinkProperties(); 742 743 IBinder s1 = ServiceManager.getService(Context.WIFI_P2P_SERVICE); 744 mWifiP2pServiceImpl = (WifiP2pServiceImpl)IWifiP2pManager.Stub.asInterface(s1); 745 746 IBinder s2 = ServiceManager.getService(Context.WIFI_PASSPOINT_SERVICE); 747 mPasspointServiceImpl = (WifiPasspointServiceImpl)IWifiPasspointManager.Stub.asInterface(s2); 748 749 mNetworkInfo.setIsAvailable(false); 750 mLastBssid = null; 751 mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; 752 mLastSignalLevel = -1; 753 754 mNetlinkTracker = new NetlinkTracker(mInterfaceName, new NetlinkTracker.Callback() { 755 public void update() { 756 sendMessage(CMD_NETLINK_UPDATE); 757 } 758 }); 759 try { 760 mNwService.registerObserver(mNetlinkTracker); 761 } catch (RemoteException e) { 762 loge("Couldn't register netlink tracker: " + e.toString()); 763 } 764 765 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); 766 Intent scanIntent = new Intent(ACTION_START_SCAN, null); 767 mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0); 768 769 Intent batchedIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null); 770 mBatchedScanIntervalIntent = PendingIntent.getBroadcast(mContext, 0, batchedIntent, 0); 771 772 mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger( 773 R.integer.config_wifi_framework_scan_interval); 774 775 mDriverStopDelayMs = mContext.getResources().getInteger( 776 R.integer.config_wifi_driver_stop_delay); 777 778 mBackgroundScanSupported = mContext.getResources().getBoolean( 779 R.bool.config_wifi_background_scan_support); 780 781 mPrimaryDeviceType = mContext.getResources().getString( 782 R.string.config_wifi_p2p_device_type); 783 784 mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(), 785 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1); 786 787 mFrameworkAutoJoin.set(Settings.Global.getInt(mContext.getContentResolver(), 788 Settings.Global.WIFI_ENHANCED_AUTO_JOIN, 1) == 1); 789 790 mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); 791 mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 792 mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 793 mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(1024 * 1024); 794 mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(1024 * 1024); 795 // TODO - needs to be a bit more dynamic 796 mNetworkCapabilities = new NetworkCapabilities(mNetworkCapabilitiesFilter); 797 798 mContext.registerReceiver( 799 new BroadcastReceiver() { 800 @Override 801 public void onReceive(Context context, Intent intent) { 802 ArrayList<String> available = intent.getStringArrayListExtra( 803 ConnectivityManager.EXTRA_AVAILABLE_TETHER); 804 ArrayList<String> active = intent.getStringArrayListExtra( 805 ConnectivityManager.EXTRA_ACTIVE_TETHER); 806 sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active)); 807 } 808 },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); 809 810 mContext.registerReceiver( 811 new BroadcastReceiver() { 812 @Override 813 public void onReceive(Context context, Intent intent) { 814 startScan(SCAN_ALARM_SOURCE, null, null); 815 if (VDBG) 816 loge("WiFiStateMachine SCAN ALARM"); 817 } 818 }, 819 new IntentFilter(ACTION_START_SCAN)); 820 821 IntentFilter filter = new IntentFilter(); 822 filter.addAction(Intent.ACTION_SCREEN_ON); 823 filter.addAction(Intent.ACTION_SCREEN_OFF); 824 filter.addAction(ACTION_REFRESH_BATCHED_SCAN); 825 mContext.registerReceiver( 826 new BroadcastReceiver() { 827 @Override 828 public void onReceive(Context context, Intent intent) { 829 String action = intent.getAction(); 830 831 if (action.equals(Intent.ACTION_SCREEN_ON)) { 832 handleScreenStateChanged(true); 833 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 834 handleScreenStateChanged(false); 835 } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) { 836 startNextBatchedScanAsync(); 837 } 838 } 839 }, filter); 840 841 mContext.registerReceiver( 842 new BroadcastReceiver() { 843 @Override 844 public void onReceive(Context context, Intent intent) { 845 int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0); 846 sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0); 847 } 848 }, 849 new IntentFilter(ACTION_DELAYED_DRIVER_STOP)); 850 851 mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( 852 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false, 853 new ContentObserver(getHandler()) { 854 @Override 855 public void onChange(boolean selfChange) { 856 mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(), 857 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1); 858 } 859 }); 860 861 mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( 862 Settings.Global.WIFI_ENHANCED_AUTO_JOIN), false, 863 new ContentObserver(getHandler()) { 864 @Override 865 public void onChange(boolean selfChange) { 866 mFrameworkAutoJoin.set(Settings.Global.getInt(mContext.getContentResolver(), 867 Settings.Global.WIFI_ENHANCED_AUTO_JOIN, 0) == 1); 868 } 869 }); 870 871 mContext.registerReceiver( 872 new BroadcastReceiver() { 873 @Override 874 public void onReceive(Context context, Intent intent) { 875 sendMessage(CMD_BOOT_COMPLETED); 876 } 877 }, 878 new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); 879 880 mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE); 881 882 PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); 883 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName()); 884 885 mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend"); 886 mSuspendWakeLock.setReferenceCounted(false); 887 888 addState(mDefaultState); 889 addState(mInitialState, mDefaultState); 890 addState(mSupplicantStartingState, mDefaultState); 891 addState(mSupplicantStartedState, mDefaultState); 892 addState(mDriverStartingState, mSupplicantStartedState); 893 addState(mDriverStartedState, mSupplicantStartedState); 894 addState(mScanModeState, mDriverStartedState); 895 addState(mConnectModeState, mDriverStartedState); 896 addState(mL2ConnectedState, mConnectModeState); 897 addState(mObtainingIpState, mL2ConnectedState); 898 addState(mVerifyingLinkState, mL2ConnectedState); 899 addState(mConnectedState, mL2ConnectedState); 900 addState(mRoamingState, mL2ConnectedState); 901 addState(mDisconnectingState, mConnectModeState); 902 addState(mDisconnectedState, mConnectModeState); 903 addState(mWpsRunningState, mConnectModeState); 904 addState(mWaitForP2pDisableState, mSupplicantStartedState); 905 addState(mDriverStoppingState, mSupplicantStartedState); 906 addState(mDriverStoppedState, mSupplicantStartedState); 907 addState(mSupplicantStoppingState, mDefaultState); 908 addState(mSoftApStartingState, mDefaultState); 909 addState(mSoftApStartedState, mDefaultState); 910 addState(mTetheringState, mSoftApStartedState); 911 addState(mTetheredState, mSoftApStartedState); 912 addState(mUntetheringState, mSoftApStartedState); 913 914 setInitialState(mInitialState); 915 916 setLogRecSize(3000); 917 setLogOnlyTransitions(false); 918 if (VDBG) setDbg(true); 919 920 //start the state machine 921 start(); 922 923 final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE); 924 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 925 intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED); 926 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 927 } 928 929 private int mVerboseLoggingLevel = 0; 930 931 int getVerboseLoggingLevel() { 932 return mVerboseLoggingLevel; 933 } 934 935 void enableVerboseLogging(int verbose) { 936 mVerboseLoggingLevel = verbose; 937 if (verbose > 0) { 938 DBG = true; 939 VDBG = true; 940 PDBG = true; 941 mLogMessages = true; 942 mWifiNative.setSupplicantLogLevel("DEBUG"); 943 } else { 944 DBG = false; 945 VDBG = false; 946 PDBG = false; 947 mLogMessages = false; 948 mWifiNative.setSupplicantLogLevel("INFO"); 949 } 950 mWifiAutoJoinController.enableVerboseLogging(verbose); 951 mWifiMonitor.enableVerboseLogging(verbose); 952 mWifiNative.enableVerboseLogging(verbose); 953 mWifiConfigStore.enableVerboseLogging(verbose); 954 mSupplicantStateTracker.enableVerboseLogging(verbose); 955 } 956 957 private int mAggressiveHandover = 0; 958 959 int getAggressiveHandover() { 960 return mAggressiveHandover; 961 } 962 963 void enableAggressiveHandover(int enabled) { 964 mAggressiveHandover = enabled; 965 } 966 967 private int mAllowScansWithTraffic = 0; 968 969 public void setAllowScansWithTraffic(int enabled) { 970 mAllowScansWithTraffic = enabled; 971 } 972 973 public int getAllowScansWithTraffic() { 974 return mAllowScansWithTraffic; 975 } 976 977 /* 978 * 979 * Framework scan control 980 */ 981 982 private boolean mAlarmEnabled = false; 983 /* This is set from the overlay config file or from a secure setting. 984 * A value of 0 disables scanning in the framework. 985 */ 986 private long mFrameworkScanIntervalMs = 10000; 987 988 private long mCurrentScanAlarmMs = 10000; 989 private void setScanAlarm(boolean enabled) { 990 if (PDBG) { 991 loge("setScanAlarm " + enabled + " period " + mCurrentScanAlarmMs); 992 } 993 if (mCurrentScanAlarmMs <= 0) enabled = false; 994 if (enabled == mAlarmEnabled) return; 995 if (enabled) { 996 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, 997 System.currentTimeMillis() + mCurrentScanAlarmMs, 998 mCurrentScanAlarmMs, 999 mScanIntent); 1000 mAlarmEnabled = true; 1001 } else { 1002 mAlarmManager.cancel(mScanIntent); 1003 mAlarmEnabled = false; 1004 } 1005 } 1006 1007 /********************************************************* 1008 * Methods exposed for public use 1009 ********************************************************/ 1010 1011 public Messenger getMessenger() { 1012 return new Messenger(getHandler()); 1013 } 1014 1015 public WifiMonitor getWifiMonitor() { 1016 return mWifiMonitor; 1017 } 1018 1019 /** 1020 * TODO: doc 1021 */ 1022 public boolean syncPingSupplicant(AsyncChannel channel) { 1023 Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT); 1024 boolean result = (resultMsg.arg1 != FAILURE); 1025 resultMsg.recycle(); 1026 return result; 1027 } 1028 1029 public List<WifiChannel> syncGetChannelList(AsyncChannel channel) { 1030 Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CAPABILITY_FREQ); 1031 List<WifiChannel> list = null; 1032 if (resultMsg.obj != null) { 1033 list = new ArrayList<WifiChannel>(); 1034 String freqs = (String) resultMsg.obj; 1035 String[] lines = freqs.split("\n"); 1036 for (String line : lines) 1037 if (line.contains("MHz")) { 1038 // line format: " 52 = 5260 MHz (NO_IBSS) (DFS)" 1039 WifiChannel c = new WifiChannel(); 1040 String[] prop = line.split(" "); 1041 if (prop.length < 5) continue; 1042 try { 1043 c.channelNum = Integer.parseInt(prop[1]); 1044 c.freqMHz = Integer.parseInt(prop[3]); 1045 } catch (NumberFormatException e) { } 1046 c.isDFS = line.contains("(DFS)"); 1047 list.add(c); 1048 } else if (line.contains("Mode[B] Channels:")) { 1049 // B channels are the same as G channels, skipped 1050 break; 1051 } 1052 } 1053 resultMsg.recycle(); 1054 return (list != null && list.size() > 0) ? list : null; 1055 } 1056 1057 1058 /** 1059 * Initiate a wifi scan. If workSource is not null, blame is given to it, otherwise blame is 1060 * given to callingUid. 1061 * 1062 * @param callingUid The uid initiating the wifi scan. Blame will be given here unless 1063 * workSource is specified. 1064 * @param workSource If not null, blame is given to workSource. 1065 * @param settings Scan settings, see {@link ScanSettings}. 1066 */ 1067 public void startScan(int callingUid, ScanSettings settings, WorkSource workSource) { 1068 Bundle bundle = new Bundle(); 1069 bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings); 1070 bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource); 1071 sendMessage(CMD_START_SCAN, callingUid, 0, bundle); 1072 } 1073 1074 /** 1075 * start or stop batched scanning using the given settings 1076 */ 1077 public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid, int csph, 1078 WorkSource workSource) { 1079 Bundle bundle = new Bundle(); 1080 bundle.putParcelable(BATCHED_SETTING, settings); 1081 bundle.putParcelable(BATCHED_WORKSOURCE, workSource); 1082 sendMessage(CMD_SET_BATCHED_SCAN, callingUid, csph, bundle); 1083 } 1084 1085 public List<BatchedScanResult> syncGetBatchedScanResultsList() { 1086 synchronized (mBatchedScanResults) { 1087 List<BatchedScanResult> batchedScanList = 1088 new ArrayList<BatchedScanResult>(mBatchedScanResults.size()); 1089 for(BatchedScanResult result: mBatchedScanResults) { 1090 batchedScanList.add(new BatchedScanResult(result)); 1091 } 1092 return batchedScanList; 1093 } 1094 } 1095 1096 public void requestBatchedScanPoll() { 1097 sendMessage(CMD_POLL_BATCHED_SCAN); 1098 } 1099 1100 private void startBatchedScan() { 1101 if (mBatchedScanSettings == null) return; 1102 1103 if (mDhcpActive) { 1104 if (DBG) log("not starting Batched Scans due to DHCP"); 1105 return; 1106 } 1107 1108 // first grab any existing data 1109 retrieveBatchedScanData(); 1110 1111 if (PDBG) loge("try starting Batched Scans due to DHCP"); 1112 1113 1114 mAlarmManager.cancel(mBatchedScanIntervalIntent); 1115 1116 String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings); 1117 try { 1118 mExpectedBatchedScans = Integer.parseInt(scansExpected); 1119 setNextBatchedAlarm(mExpectedBatchedScans); 1120 if (mExpectedBatchedScans > 0) noteBatchedScanStart(); 1121 } catch (NumberFormatException e) { 1122 stopBatchedScan(); 1123 loge("Exception parsing WifiNative.setBatchedScanSettings response " + e); 1124 } 1125 } 1126 1127 // called from BroadcastListener 1128 private void startNextBatchedScanAsync() { 1129 sendMessage(CMD_START_NEXT_BATCHED_SCAN); 1130 } 1131 1132 private void startNextBatchedScan() { 1133 // first grab any existing data 1134 retrieveBatchedScanData(); 1135 1136 setNextBatchedAlarm(mExpectedBatchedScans); 1137 } 1138 1139 private void handleBatchedScanPollRequest() { 1140 if (DBG) { 1141 log("handleBatchedScanPoll Request - mBatchedScanMinPollTime=" + 1142 mBatchedScanMinPollTime + " , mBatchedScanSettings=" + 1143 mBatchedScanSettings); 1144 } 1145 // if there is no appropriate PollTime that's because we either aren't 1146 // batching or we've already set a time for a poll request 1147 if (mBatchedScanMinPollTime == 0) return; 1148 if (mBatchedScanSettings == null) return; 1149 1150 long now = System.currentTimeMillis(); 1151 1152 if (now > mBatchedScanMinPollTime) { 1153 // do the poll and reset our timers 1154 startNextBatchedScan(); 1155 } else { 1156 mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, mBatchedScanMinPollTime, 1157 mBatchedScanIntervalIntent); 1158 mBatchedScanMinPollTime = 0; 1159 } 1160 } 1161 1162 // return true if new/different 1163 private boolean recordBatchedScanSettings(int responsibleUid, int csph, Bundle bundle) { 1164 BatchedScanSettings settings = bundle.getParcelable(BATCHED_SETTING); 1165 WorkSource responsibleWorkSource = bundle.getParcelable(BATCHED_WORKSOURCE); 1166 1167 if (DBG) { 1168 log("set batched scan to " + settings + " for uid=" + responsibleUid + 1169 ", worksource=" + responsibleWorkSource); 1170 } 1171 if (settings != null) { 1172 if (settings.equals(mBatchedScanSettings)) return false; 1173 } else { 1174 if (mBatchedScanSettings == null) return false; 1175 } 1176 mBatchedScanSettings = settings; 1177 if (responsibleWorkSource == null) responsibleWorkSource = new WorkSource(responsibleUid); 1178 mBatchedScanWorkSource = responsibleWorkSource; 1179 mBatchedScanCsph = csph; 1180 return true; 1181 } 1182 1183 private void stopBatchedScan() { 1184 mAlarmManager.cancel(mBatchedScanIntervalIntent); 1185 retrieveBatchedScanData(); 1186 mWifiNative.setBatchedScanSettings(null); 1187 noteBatchedScanStop(); 1188 } 1189 1190 private void setNextBatchedAlarm(int scansExpected) { 1191 1192 if (mBatchedScanSettings == null || scansExpected < 1) return; 1193 1194 mBatchedScanMinPollTime = System.currentTimeMillis() + 1195 mBatchedScanSettings.scanIntervalSec * 1000; 1196 1197 if (mBatchedScanSettings.maxScansPerBatch < scansExpected) { 1198 scansExpected = mBatchedScanSettings.maxScansPerBatch; 1199 } 1200 1201 int secToFull = mBatchedScanSettings.scanIntervalSec; 1202 secToFull *= scansExpected; 1203 1204 int debugPeriod = SystemProperties.getInt("wifi.batchedScan.pollPeriod", 0); 1205 if (debugPeriod > 0) secToFull = debugPeriod; 1206 1207 // set the alarm to do the next poll. We set it a little short as we'd rather 1208 // wake up wearly than miss a scan due to buffer overflow 1209 mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() 1210 + ((secToFull - (mBatchedScanSettings.scanIntervalSec / 2)) * 1000), 1211 mBatchedScanIntervalIntent); 1212 } 1213 1214 /** 1215 * Start reading new scan data 1216 * Data comes in as: 1217 * "scancount=5\n" 1218 * "nextcount=5\n" 1219 * "apcount=3\n" 1220 * "trunc\n" (optional) 1221 * "bssid=...\n" 1222 * "ssid=...\n" 1223 * "freq=...\n" (in Mhz) 1224 * "level=...\n" 1225 * "dist=...\n" (in cm) 1226 * "distsd=...\n" (standard deviation, in cm) 1227 * "====" 1228 * "bssid=...\n" 1229 * etc 1230 * "====" 1231 * "bssid=...\n" 1232 * etc 1233 * "%%%%" 1234 * "apcount=2\n" 1235 * "bssid=...\n" 1236 * etc 1237 * "%%%% 1238 * etc 1239 * "----" 1240 */ 1241 private final static boolean DEBUG_PARSE = false; 1242 private void retrieveBatchedScanData() { 1243 String rawData = mWifiNative.getBatchedScanResults(); 1244 if (DEBUG_PARSE) log("rawData = " + rawData); 1245 mBatchedScanMinPollTime = 0; 1246 if (rawData == null || rawData.equalsIgnoreCase("OK")) { 1247 loge("Unexpected BatchedScanResults :" + rawData); 1248 return; 1249 } 1250 1251 int scanCount = 0; 1252 final String END_OF_BATCHES = "----"; 1253 final String SCANCOUNT = "scancount="; 1254 final String TRUNCATED = "trunc"; 1255 final String AGE = "age="; 1256 final String DIST = "dist="; 1257 final String DISTSD = "distSd="; 1258 1259 String splitData[] = rawData.split("\n"); 1260 int n = 0; 1261 if (splitData[n].startsWith(SCANCOUNT)) { 1262 try { 1263 scanCount = Integer.parseInt(splitData[n++].substring(SCANCOUNT.length())); 1264 } catch (NumberFormatException e) { 1265 loge("scancount parseInt Exception from " + splitData[n]); 1266 } 1267 } else log("scancount not found"); 1268 if (scanCount == 0) { 1269 loge("scanCount==0 - aborting"); 1270 return; 1271 } 1272 1273 final Intent intent = new Intent(WifiManager.BATCHED_SCAN_RESULTS_AVAILABLE_ACTION); 1274 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 1275 1276 synchronized (mBatchedScanResults) { 1277 mBatchedScanResults.clear(); 1278 BatchedScanResult batchedScanResult = new BatchedScanResult(); 1279 1280 String bssid = null; 1281 WifiSsid wifiSsid = null; 1282 int level = 0; 1283 int freq = 0; 1284 int dist, distSd; 1285 long tsf = 0; 1286 dist = distSd = ScanResult.UNSPECIFIED; 1287 final long now = SystemClock.elapsedRealtime(); 1288 final int bssidStrLen = BSSID_STR.length(); 1289 1290 while (true) { 1291 while (n < splitData.length) { 1292 if (DEBUG_PARSE) logd("parsing " + splitData[n]); 1293 if (splitData[n].equals(END_OF_BATCHES)) { 1294 if (n+1 != splitData.length) { 1295 loge("didn't consume " + (splitData.length-n)); 1296 } 1297 if (mBatchedScanResults.size() > 0) { 1298 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 1299 } 1300 logd("retrieveBatchedScanResults X"); 1301 return; 1302 } 1303 if ((splitData[n].equals(END_STR)) || splitData[n].equals(DELIMITER_STR)) { 1304 if (bssid != null) { 1305 batchedScanResult.scanResults.add(new ScanResult( 1306 wifiSsid, bssid, "", level, freq, tsf, dist, distSd)); 1307 wifiSsid = null; 1308 bssid = null; 1309 level = 0; 1310 freq = 0; 1311 tsf = 0; 1312 dist = distSd = ScanResult.UNSPECIFIED; 1313 } 1314 if (splitData[n].equals(END_STR)) { 1315 if (batchedScanResult.scanResults.size() != 0) { 1316 mBatchedScanResults.add(batchedScanResult); 1317 batchedScanResult = new BatchedScanResult(); 1318 } else { 1319 logd("Found empty batch"); 1320 } 1321 } 1322 } else if (splitData[n].equals(TRUNCATED)) { 1323 batchedScanResult.truncated = true; 1324 } else if (splitData[n].startsWith(BSSID_STR)) { 1325 bssid = new String(splitData[n].getBytes(), bssidStrLen, 1326 splitData[n].length() - bssidStrLen); 1327 } else if (splitData[n].startsWith(FREQ_STR)) { 1328 try { 1329 freq = Integer.parseInt(splitData[n].substring(FREQ_STR.length())); 1330 } catch (NumberFormatException e) { 1331 loge("Invalid freqency: " + splitData[n]); 1332 freq = 0; 1333 } 1334 } else if (splitData[n].startsWith(AGE)) { 1335 try { 1336 tsf = now - Long.parseLong(splitData[n].substring(AGE.length())); 1337 tsf *= 1000; // convert mS -> uS 1338 } catch (NumberFormatException e) { 1339 loge("Invalid timestamp: " + splitData[n]); 1340 tsf = 0; 1341 } 1342 } else if (splitData[n].startsWith(SSID_STR)) { 1343 wifiSsid = WifiSsid.createFromAsciiEncoded( 1344 splitData[n].substring(SSID_STR.length())); 1345 } else if (splitData[n].startsWith(LEVEL_STR)) { 1346 try { 1347 level = Integer.parseInt(splitData[n].substring(LEVEL_STR.length())); 1348 if (level > 0) level -= 256; 1349 } catch (NumberFormatException e) { 1350 loge("Invalid level: " + splitData[n]); 1351 level = 0; 1352 } 1353 } else if (splitData[n].startsWith(DIST)) { 1354 try { 1355 dist = Integer.parseInt(splitData[n].substring(DIST.length())); 1356 } catch (NumberFormatException e) { 1357 loge("Invalid distance: " + splitData[n]); 1358 dist = ScanResult.UNSPECIFIED; 1359 } 1360 } else if (splitData[n].startsWith(DISTSD)) { 1361 try { 1362 distSd = Integer.parseInt(splitData[n].substring(DISTSD.length())); 1363 } catch (NumberFormatException e) { 1364 loge("Invalid distanceSd: " + splitData[n]); 1365 distSd = ScanResult.UNSPECIFIED; 1366 } 1367 } else { 1368 loge("Unable to parse batched scan result line: " + splitData[n]); 1369 } 1370 n++; 1371 } 1372 rawData = mWifiNative.getBatchedScanResults(); 1373 if (DEBUG_PARSE) log("reading more data:\n" + rawData); 1374 if (rawData == null) { 1375 loge("Unexpected null BatchedScanResults"); 1376 return; 1377 } 1378 splitData = rawData.split("\n"); 1379 if (splitData.length == 0 || splitData[0].equals("ok")) { 1380 loge("batch scan results just ended!"); 1381 if (mBatchedScanResults.size() > 0) { 1382 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1383 } 1384 return; 1385 } 1386 n = 0; 1387 } 1388 } 1389 } 1390 1391 //keeping track of scan requests 1392 private long lastStartScanTimeStamp = 0; 1393 private long lastScanDuration = 0; 1394 //last connect attempt is used to prevent scan requests: 1395 // - for a period of 10 seconds after attempting to connect 1396 private long lastConnectAttempt = 0; 1397 private String lastScanFreqs = null; 1398 1399 //TODO: this is used only to track connection attempts, however the link state and packet per 1400 //TODO: second logic should be folded into that 1401 private boolean isScanAllowed() { 1402 long now = System.currentTimeMillis(); 1403 if (lastConnectAttempt != 0 && (now - lastConnectAttempt) < 10000) { 1404 return false; 1405 } 1406 return true; 1407 } 1408 1409 // If workSource is not null, blame is given to it, otherwise blame is given to callingUid. 1410 private void noteScanStart(int callingUid, WorkSource workSource) { 1411 long now = System.currentTimeMillis(); 1412 lastStartScanTimeStamp = now; 1413 lastScanDuration = 0; 1414 if (DBG) { 1415 String ts = String.format("[%,d ms]", now); 1416 if (workSource != null) { 1417 loge(ts + " noteScanStart" + workSource.toString() 1418 + " uid " + Integer.toString(callingUid)); 1419 } else { 1420 loge(ts + " noteScanstart no scan source"); 1421 } 1422 } 1423 if (mScanWorkSource == null && ((callingUid != UNKNOWN_SCAN_SOURCE 1424 && callingUid != SCAN_ALARM_SOURCE) 1425 || workSource != null)) { 1426 mScanWorkSource = workSource != null ? workSource : new WorkSource(callingUid); 1427 try { 1428 mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource); 1429 } catch (RemoteException e) { 1430 log(e.toString()); 1431 } 1432 } 1433 } 1434 1435 private void noteScanEnd() { 1436 long now = System.currentTimeMillis(); 1437 if (lastStartScanTimeStamp != 0) { 1438 lastScanDuration = now - lastStartScanTimeStamp; 1439 } 1440 lastStartScanTimeStamp = 0; 1441 if (DBG) { 1442 String ts = String.format("[%,d ms]", now); 1443 1444 if (mScanWorkSource != null) 1445 loge(ts + " noteScanEnd " + mScanWorkSource.toString()); 1446 else 1447 loge(ts + " noteScanEnd no scan source"); 1448 } 1449 if (mScanWorkSource != null) { 1450 try { 1451 mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource); 1452 } catch (RemoteException e) { 1453 log(e.toString()); 1454 } finally { 1455 mScanWorkSource = null; 1456 } 1457 } 1458 } 1459 1460 private void noteBatchedScanStart() { 1461 if (PDBG) loge("noteBatchedScanstart()"); 1462 // note the end of a previous scan set 1463 if (mNotedBatchedScanWorkSource != null && 1464 (mNotedBatchedScanWorkSource.equals(mBatchedScanWorkSource) == false || 1465 mNotedBatchedScanCsph != mBatchedScanCsph)) { 1466 try { 1467 mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource); 1468 } catch (RemoteException e) { 1469 log(e.toString()); 1470 } finally { 1471 mNotedBatchedScanWorkSource = null; 1472 mNotedBatchedScanCsph = 0; 1473 } 1474 } 1475 // note the start of the new 1476 try { 1477 mBatteryStats.noteWifiBatchedScanStartedFromSource(mBatchedScanWorkSource, 1478 mBatchedScanCsph); 1479 mNotedBatchedScanWorkSource = mBatchedScanWorkSource; 1480 mNotedBatchedScanCsph = mBatchedScanCsph; 1481 } catch (RemoteException e) { 1482 log(e.toString()); 1483 } 1484 } 1485 1486 private void noteBatchedScanStop() { 1487 if (PDBG) loge("noteBatchedScanstop()"); 1488 1489 if (mNotedBatchedScanWorkSource != null) { 1490 try { 1491 mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource); 1492 } catch (RemoteException e) { 1493 log(e.toString()); 1494 } finally { 1495 mNotedBatchedScanWorkSource = null; 1496 mNotedBatchedScanCsph = 0; 1497 } 1498 } 1499 } 1500 1501 private void handleScanRequest(int type, Message message) { 1502 // unbundle parameters 1503 Bundle bundle = (Bundle) message.obj; 1504 ScanSettings settings = bundle.getParcelable(CUSTOMIZED_SCAN_SETTING); 1505 WorkSource workSource = bundle.getParcelable(CUSTOMIZED_SCAN_WORKSOURCE); 1506 1507 // parse scan settings 1508 String freqs = null; 1509 if (settings != null && settings.channelSet != null) { 1510 StringBuilder sb = new StringBuilder(); 1511 boolean first = true; 1512 for (WifiChannel channel : settings.channelSet) { 1513 if (!first) sb.append(','); else first = false; 1514 sb.append(channel.freqMHz); 1515 } 1516 freqs = sb.toString(); 1517 } 1518 1519 // call wifi native to start the scan 1520 if (startScanNative(type, freqs)) { 1521 // only count battery consumption if scan request is accepted 1522 noteScanStart(message.arg1, workSource); 1523 // a full scan covers everything, clearing scan request buffer 1524 if (freqs == null) 1525 mBufferedScanMsg.clear(); 1526 return; 1527 } 1528 1529 // if reach here, scan request is rejected 1530 1531 if (!mIsScanOngoing) { 1532 // if rejection is NOT due to ongoing scan (e.g. bad scan parameters), 1533 // discard this request and pop up the next one 1534 if (mBufferedScanMsg.size() > 0) 1535 sendMessage(mBufferedScanMsg.remove()); 1536 } else if (!mIsFullScanOngoing) { 1537 // if rejection is due to an ongoing scan, and the ongoing one is NOT a full scan, 1538 // buffer the scan request to make sure specified channels will be scanned eventually 1539 if (freqs == null) 1540 mBufferedScanMsg.clear(); 1541 if (mBufferedScanMsg.size() < SCAN_REQUEST_BUFFER_MAX_SIZE) { 1542 Message msg = obtainMessage(CMD_START_SCAN, message.arg1, 0, bundle); 1543 mBufferedScanMsg.add(msg); 1544 } else { 1545 // if too many requests in buffer, combine them into a single full scan 1546 bundle = new Bundle(); 1547 bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, null); 1548 bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource); 1549 Message msg = obtainMessage(CMD_START_SCAN, message.arg1, 0, bundle); 1550 mBufferedScanMsg.clear(); 1551 mBufferedScanMsg.add(msg); 1552 } 1553 } 1554 } 1555 1556 1557 /** return true iff scan request is accepted */ 1558 private boolean startScanNative(int type, String freqs) { 1559 if (mWifiNative.scan(type, freqs)) { 1560 mIsScanOngoing = true; 1561 mIsFullScanOngoing = (freqs == null); 1562 lastScanFreqs = freqs; 1563 return true; 1564 } 1565 return false; 1566 } 1567 1568 /** 1569 * TODO: doc 1570 */ 1571 public void setSupplicantRunning(boolean enable) { 1572 if (enable) { 1573 sendMessage(CMD_START_SUPPLICANT); 1574 } else { 1575 sendMessage(CMD_STOP_SUPPLICANT); 1576 } 1577 } 1578 1579 /** 1580 * TODO: doc 1581 */ 1582 public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) { 1583 if (enable) { 1584 sendMessage(CMD_START_AP, wifiConfig); 1585 } else { 1586 sendMessage(CMD_STOP_AP); 1587 } 1588 } 1589 1590 public void setWifiApConfiguration(WifiConfiguration config) { 1591 mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config); 1592 } 1593 1594 public WifiConfiguration syncGetWifiApConfiguration() { 1595 Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG); 1596 WifiConfiguration ret = (WifiConfiguration) resultMsg.obj; 1597 resultMsg.recycle(); 1598 return ret; 1599 } 1600 1601 /** 1602 * TODO: doc 1603 */ 1604 public int syncGetWifiState() { 1605 return mWifiState.get(); 1606 } 1607 1608 /** 1609 * TODO: doc 1610 */ 1611 public String syncGetWifiStateByName() { 1612 switch (mWifiState.get()) { 1613 case WIFI_STATE_DISABLING: 1614 return "disabling"; 1615 case WIFI_STATE_DISABLED: 1616 return "disabled"; 1617 case WIFI_STATE_ENABLING: 1618 return "enabling"; 1619 case WIFI_STATE_ENABLED: 1620 return "enabled"; 1621 case WIFI_STATE_UNKNOWN: 1622 return "unknown state"; 1623 default: 1624 return "[invalid state]"; 1625 } 1626 } 1627 1628 /** 1629 * TODO: doc 1630 */ 1631 public int syncGetWifiApState() { 1632 return mWifiApState.get(); 1633 } 1634 1635 /** 1636 * TODO: doc 1637 */ 1638 public String syncGetWifiApStateByName() { 1639 switch (mWifiApState.get()) { 1640 case WIFI_AP_STATE_DISABLING: 1641 return "disabling"; 1642 case WIFI_AP_STATE_DISABLED: 1643 return "disabled"; 1644 case WIFI_AP_STATE_ENABLING: 1645 return "enabling"; 1646 case WIFI_AP_STATE_ENABLED: 1647 return "enabled"; 1648 case WIFI_AP_STATE_FAILED: 1649 return "failed"; 1650 default: 1651 return "[invalid state]"; 1652 } 1653 } 1654 1655 /** 1656 * Get status information for the current connection, if any. 1657 * @return a {@link WifiInfo} object containing information about the current connection 1658 * 1659 */ 1660 public WifiInfo syncRequestConnectionInfo() { 1661 return mWifiInfo; 1662 } 1663 1664 public DhcpResults syncGetDhcpResults() { 1665 synchronized (mDhcpResultsLock) { 1666 return new DhcpResults(mDhcpResults); 1667 } 1668 } 1669 1670 /** 1671 * TODO: doc 1672 */ 1673 public void setDriverStart(boolean enable) { 1674 if (enable) { 1675 sendMessage(CMD_START_DRIVER); 1676 } else { 1677 sendMessage(CMD_STOP_DRIVER); 1678 } 1679 } 1680 1681 /** 1682 * TODO: doc 1683 */ 1684 public void setOperationalMode(int mode) { 1685 if (DBG) log("setting operational mode to " + String.valueOf(mode)); 1686 sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0); 1687 } 1688 1689 /** 1690 * TODO: doc 1691 */ 1692 public List<ScanResult> syncGetScanResultsList() { 1693 synchronized (mScanResultCache) { 1694 List<ScanResult> scanList = new ArrayList<ScanResult>(); 1695 for(ScanResult result: mScanResults) { 1696 scanList.add(new ScanResult(result)); 1697 } 1698 return scanList; 1699 } 1700 } 1701 1702 /** 1703 * Disconnect from Access Point 1704 */ 1705 public void disconnectCommand() { 1706 sendMessage(CMD_DISCONNECT); 1707 } 1708 1709 /** 1710 * Initiate a reconnection to AP 1711 */ 1712 public void reconnectCommand() { 1713 sendMessage(CMD_RECONNECT); 1714 } 1715 1716 /** 1717 * Initiate a re-association to AP 1718 */ 1719 public void reassociateCommand() { 1720 sendMessage(CMD_REASSOCIATE); 1721 } 1722 1723 /** 1724 * Reload networks and then reconnect; helps load correct data for TLS networks 1725 */ 1726 1727 public void reloadTlsNetworksAndReconnect() { 1728 sendMessage(CMD_RELOAD_TLS_AND_RECONNECT); 1729 } 1730 1731 /** 1732 * Add a network synchronously 1733 * 1734 * @return network id of the new network 1735 */ 1736 public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) { 1737 Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config); 1738 int result = resultMsg.arg1; 1739 resultMsg.recycle(); 1740 return result; 1741 } 1742 1743 public List<WifiConfiguration> syncGetConfiguredNetworks(AsyncChannel channel) { 1744 Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS); 1745 List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj; 1746 resultMsg.recycle(); 1747 return result; 1748 } 1749 1750 /** 1751 * Delete a network 1752 * 1753 * @param networkId id of the network to be removed 1754 */ 1755 public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) { 1756 Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId); 1757 boolean result = (resultMsg.arg1 != FAILURE); 1758 resultMsg.recycle(); 1759 return result; 1760 } 1761 1762 /** 1763 * Enable a network 1764 * 1765 * @param netId network id of the network 1766 * @param disableOthers true, if all other networks have to be disabled 1767 * @return {@code true} if the operation succeeds, {@code false} otherwise 1768 */ 1769 public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) { 1770 Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId, 1771 disableOthers ? 1 : 0); 1772 boolean result = (resultMsg.arg1 != FAILURE); 1773 resultMsg.recycle(); 1774 return result; 1775 } 1776 1777 /** 1778 * Disable a network 1779 * 1780 * @param netId network id of the network 1781 * @return {@code true} if the operation succeeds, {@code false} otherwise 1782 */ 1783 public boolean syncDisableNetwork(AsyncChannel channel, int netId) { 1784 Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId); 1785 boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED); 1786 resultMsg.recycle(); 1787 return result; 1788 } 1789 1790 /** 1791 * Retrieves a WPS-NFC configuration token for the specified network 1792 * @return a hex string representation of the WPS-NFC configuration token 1793 */ 1794 public String syncGetWpsNfcConfigurationToken(int netId) { 1795 return mWifiNative.getNfcWpsConfigurationToken(netId); 1796 } 1797 1798 /** 1799 * Blacklist a BSSID. This will avoid the AP if there are 1800 * alternate APs to connect 1801 * 1802 * @param bssid BSSID of the network 1803 */ 1804 public void addToBlacklist(String bssid) { 1805 sendMessage(CMD_BLACKLIST_NETWORK, bssid); 1806 } 1807 1808 /** 1809 * Clear the blacklist list 1810 * 1811 */ 1812 public void clearBlacklist() { 1813 sendMessage(CMD_CLEAR_BLACKLIST); 1814 } 1815 1816 public void enableRssiPolling(boolean enabled) { 1817 sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0); 1818 } 1819 1820 public void enableBackgroundScanCommand(boolean enabled) { 1821 sendMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0); 1822 } 1823 1824 public void enableAllNetworks() { 1825 sendMessage(CMD_ENABLE_ALL_NETWORKS); 1826 } 1827 1828 /** 1829 * Start filtering Multicast v4 packets 1830 */ 1831 public void startFilteringMulticastV4Packets() { 1832 mFilteringMulticastV4Packets.set(true); 1833 sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0); 1834 } 1835 1836 /** 1837 * Stop filtering Multicast v4 packets 1838 */ 1839 public void stopFilteringMulticastV4Packets() { 1840 mFilteringMulticastV4Packets.set(false); 1841 sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0); 1842 } 1843 1844 /** 1845 * Start filtering Multicast v4 packets 1846 */ 1847 public void startFilteringMulticastV6Packets() { 1848 sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0); 1849 } 1850 1851 /** 1852 * Stop filtering Multicast v4 packets 1853 */ 1854 public void stopFilteringMulticastV6Packets() { 1855 sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0); 1856 } 1857 1858 /** 1859 * Set high performance mode of operation. 1860 * Enabling would set active power mode and disable suspend optimizations; 1861 * disabling would set auto power mode and enable suspend optimizations 1862 * @param enable true if enable, false otherwise 1863 */ 1864 public void setHighPerfModeEnabled(boolean enable) { 1865 sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0); 1866 } 1867 1868 /** 1869 * Set the country code 1870 * @param countryCode following ISO 3166 format 1871 * @param persist {@code true} if the setting should be remembered. 1872 */ 1873 public void setCountryCode(String countryCode, boolean persist) { 1874 // If it's a good country code, apply after the current 1875 // wifi connection is terminated; ignore resetting of code 1876 // for now (it is unclear what the chipset should do when 1877 // country code is reset) 1878 int countryCodeSequence = mCountryCodeSequence.incrementAndGet(); 1879 if (TextUtils.isEmpty(countryCode)) { 1880 log("Ignoring resetting of country code"); 1881 } else { 1882 sendMessage(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0, countryCode); 1883 } 1884 } 1885 1886 /** 1887 * Set the operational frequency band 1888 * @param band 1889 * @param persist {@code true} if the setting should be remembered. 1890 */ 1891 public void setFrequencyBand(int band, boolean persist) { 1892 if (persist) { 1893 Settings.Global.putInt(mContext.getContentResolver(), 1894 Settings.Global.WIFI_FREQUENCY_BAND, 1895 band); 1896 } 1897 sendMessage(CMD_SET_FREQUENCY_BAND, band, 0); 1898 } 1899 1900 /** 1901 * Enable TDLS for a specific MAC address 1902 */ 1903 public void enableTdls(String remoteMacAddress, boolean enable) { 1904 int enabler = enable ? 1 : 0; 1905 sendMessage(CMD_ENABLE_TDLS, enabler, 0, remoteMacAddress); 1906 } 1907 1908 /** 1909 * Returns the operational frequency band 1910 */ 1911 public int getFrequencyBand() { 1912 return mFrequencyBand.get(); 1913 } 1914 1915 /** 1916 * Returns the wifi configuration file 1917 */ 1918 public String getConfigFile() { 1919 return mWifiConfigStore.getConfigFile(); 1920 } 1921 1922 /** 1923 * Send a message indicating bluetooth adapter connection state changed 1924 */ 1925 public void sendBluetoothAdapterStateChange(int state) { 1926 sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0); 1927 } 1928 1929 /** 1930 * Save configuration on supplicant 1931 * 1932 * @return {@code true} if the operation succeeds, {@code false} otherwise 1933 * 1934 * TODO: deprecate this 1935 */ 1936 public boolean syncSaveConfig(AsyncChannel channel) { 1937 Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG); 1938 boolean result = (resultMsg.arg1 != FAILURE); 1939 resultMsg.recycle(); 1940 return result; 1941 } 1942 1943 public void updateBatteryWorkSource(WorkSource newSource) { 1944 synchronized (mRunningWifiUids) { 1945 try { 1946 if (newSource != null) { 1947 mRunningWifiUids.set(newSource); 1948 } 1949 if (mIsRunning) { 1950 if (mReportedRunning) { 1951 // If the work source has changed since last time, need 1952 // to remove old work from battery stats. 1953 if (mLastRunningWifiUids.diff(mRunningWifiUids)) { 1954 mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids, 1955 mRunningWifiUids); 1956 mLastRunningWifiUids.set(mRunningWifiUids); 1957 } 1958 } else { 1959 // Now being started, report it. 1960 mBatteryStats.noteWifiRunning(mRunningWifiUids); 1961 mLastRunningWifiUids.set(mRunningWifiUids); 1962 mReportedRunning = true; 1963 } 1964 } else { 1965 if (mReportedRunning) { 1966 // Last reported we were running, time to stop. 1967 mBatteryStats.noteWifiStopped(mLastRunningWifiUids); 1968 mLastRunningWifiUids.clear(); 1969 mReportedRunning = false; 1970 } 1971 } 1972 mWakeLock.setWorkSource(newSource); 1973 } catch (RemoteException ignore) { 1974 } 1975 } 1976 } 1977 1978 @Override 1979 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1980 super.dump(fd, pw, args); 1981 mSupplicantStateTracker.dump(fd, pw, args); 1982 pw.println("mLinkProperties " + mLinkProperties); 1983 pw.println("mWifiInfo " + mWifiInfo); 1984 pw.println("mDhcpResults " + mDhcpResults); 1985 pw.println("mNetworkInfo " + mNetworkInfo); 1986 pw.println("mLastSignalLevel " + mLastSignalLevel); 1987 pw.println("mLastBssid " + mLastBssid); 1988 pw.println("mLastNetworkId " + mLastNetworkId); 1989 pw.println("mReconnectCount " + mReconnectCount); 1990 pw.println("mOperationalMode " + mOperationalMode); 1991 pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt); 1992 pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled); 1993 pw.println("Supplicant status " + mWifiNative.status()); 1994 pw.println("mEnableBackgroundScan " + mEnableBackgroundScan); 1995 pw.println("mLastSetCountryCode " + mLastSetCountryCode); 1996 pw.println("mPersistedCountryCode " + mPersistedCountryCode); 1997 pw.println(); 1998 mWifiConfigStore.dump(fd, pw, args); 1999 } 2000 2001 /********************************************************* 2002 * Internal private functions 2003 ********************************************************/ 2004 2005 private void logStateAndMessage(Message message, String state) { 2006 if (mLogMessages) { 2007 //long now = SystemClock.elapsedRealtimeNanos(); 2008 //String ts = String.format("[%,d us]", now/1000); 2009 2010 loge( " " + state + " " + getLogRecString(message)); 2011 } 2012 } 2013 2014 /** 2015 * Return the additional string to be logged by LogRec, default 2016 * 2017 * @param msg that was processed 2018 * @return information to be logged as a String 2019 */ 2020 protected String getLogRecString(Message msg) { 2021 WifiConfiguration config; 2022 StringBuilder sb = new StringBuilder(); 2023 if (mScreenOn) { 2024 sb.append("!"); 2025 } 2026 sb.append(smToString(msg)); 2027 2028 switch (msg.what) { 2029 case CMD_START_SCAN: 2030 sb.append(" "); 2031 sb.append(Integer.toString(msg.arg1)); 2032 sb.append(" "); 2033 sb.append(Integer.toString(msg.arg2)); 2034 if (mIsScanOngoing) sb.append(" onGoing"); 2035 if (mIsFullScanOngoing) sb.append(" full"); 2036 if (lastStartScanTimeStamp != 0) { 2037 sb.append(" started:").append(lastStartScanTimeStamp); 2038 } 2039 if (lastScanDuration != 0) { 2040 sb.append(" dur:").append(lastScanDuration); 2041 } 2042 sb.append(" rssi=").append(mWifiInfo.getRssi()); 2043 sb.append(" f=").append(mWifiInfo.getFrequency()); 2044 sb.append(" sc=").append(mWifiInfo.score); 2045 sb.append(" link=").append(mWifiInfo.getLinkSpeed()); 2046 sb.append(String.format(" tx=%.1f,", mWifiInfo.txSuccessRate)); 2047 sb.append(String.format(" %.1f,", mWifiInfo.txRetriesRate)); 2048 sb.append(String.format(" %.1f ", mWifiInfo.txBadRate)); 2049 sb.append(String.format(" rx=%.1f", mWifiInfo.rxSuccessRate)); 2050 if (lastScanFreqs != null) sb.append(lastScanFreqs); 2051 break; 2052 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 2053 sb.append(" "); 2054 sb.append(Integer.toString(msg.arg1)); 2055 sb.append(" "); 2056 sb.append(Integer.toString(msg.arg2)); 2057 StateChangeResult stateChangeResult = (StateChangeResult) msg.obj; 2058 if (stateChangeResult != null) { 2059 sb.append(stateChangeResult.toString()); 2060 } 2061 break; 2062 case WifiMonitor.ASSOCIATION_REJECTION_EVENT: 2063 sb.append(" "); 2064 sb.append(Integer.toString(msg.arg1)); 2065 sb.append(" "); 2066 sb.append(Integer.toString(msg.arg2)); 2067 String bssid = (String)msg.obj; 2068 if (bssid != null && bssid.length()>0) { 2069 sb.append(" "); 2070 sb.append(bssid); 2071 } 2072 break; 2073 case WifiMonitor.SCAN_RESULTS_EVENT: 2074 sb.append(" "); 2075 sb.append(Integer.toString(msg.arg1)); 2076 sb.append(" "); 2077 sb.append(Integer.toString(msg.arg2)); 2078 if (mScanResults != null) { 2079 sb.append(mScanResults.size()); 2080 } 2081 if (lastScanDuration != 0) { 2082 sb.append(" dur:").append(lastScanDuration); 2083 } 2084 break; 2085 case WifiMonitor.NETWORK_CONNECTION_EVENT: 2086 sb.append(" "); 2087 sb.append(Integer.toString(msg.arg1)); 2088 sb.append(" "); 2089 sb.append(Integer.toString(msg.arg2)); 2090 sb.append(" ").append(mLastBssid); 2091 sb.append(" nid=").append(mLastNetworkId); 2092 config = getCurrentWifiConfiguration(); 2093 if (config != null) { 2094 sb.append(" ").append(config.configKey()); 2095 } 2096 break; 2097 case WifiMonitor.NETWORK_DISCONNECTION_EVENT: 2098 if (msg.obj != null) { 2099 sb.append(" ").append((String)msg.obj); 2100 } 2101 sb.append(" nid=").append(msg.arg1); 2102 sb.append(" reason=").append(msg.arg2); 2103 break; 2104 case WifiMonitor.SSID_TEMP_DISABLED: 2105 case WifiMonitor.SSID_REENABLED: 2106 sb.append(" nid=").append(msg.arg1); 2107 if (msg.obj != null) { 2108 sb.append(" ").append((String)msg.obj); 2109 } 2110 break; 2111 case CMD_RSSI_POLL: 2112 case CMD_UNWANTED_NETWORK: 2113 sb.append(" "); 2114 sb.append(Integer.toString(msg.arg1)); 2115 sb.append(" "); 2116 sb.append(Integer.toString(msg.arg2)); 2117 sb.append(" rssi=").append(mWifiInfo.getRssi()); 2118 sb.append(" f=").append(mWifiInfo.getFrequency()); 2119 sb.append(" sc=").append(mWifiInfo.score); 2120 sb.append(" link=").append(mWifiInfo.getLinkSpeed()); 2121 sb.append(String.format(" tx=%.1f,", mWifiInfo.txSuccessRate)); 2122 sb.append(String.format(" %.1f,", mWifiInfo.txRetriesRate)); 2123 sb.append(String.format(" %.1f ", mWifiInfo.txBadRate)); 2124 sb.append(String.format(" rx=%.1f", mWifiInfo.rxSuccessRate)); 2125 break; 2126 case CMD_AUTO_CONNECT: 2127 case WifiManager.CONNECT_NETWORK: 2128 sb.append(" "); 2129 sb.append(Integer.toString(msg.arg1)); 2130 sb.append(" "); 2131 sb.append(Integer.toString(msg.arg2)); 2132 config = (WifiConfiguration) msg.obj; 2133 if (config != null) { 2134 sb.append(" ").append(config.configKey()); 2135 if (config.visibility != null) { 2136 sb.append(" [").append(config.visibility.num24); 2137 sb.append(" ,").append(config.visibility.rssi24); 2138 sb.append(" ;").append(config.visibility.num5); 2139 sb.append(" ,").append(config.visibility.rssi5).append("]"); 2140 } 2141 } 2142 break; 2143 case CMD_AUTO_ROAM: 2144 sb.append(" "); 2145 sb.append(Integer.toString(msg.arg1)); 2146 sb.append(" "); 2147 sb.append(Integer.toString(msg.arg2)); 2148 ScanResult result = (ScanResult)msg.obj; 2149 if (result != null) { 2150 sb.append(" rssi=").append(result.level); 2151 sb.append(" freq=").append(result.frequency); 2152 sb.append(" ").append(result.BSSID); 2153 } 2154 break; 2155 case CMD_ENABLE_NETWORK: 2156 sb.append(" "); 2157 sb.append(Integer.toString(msg.arg1)); 2158 sb.append(" "); 2159 sb.append(Integer.toString(msg.arg2)); 2160 String key = mWifiConfigStore.getLastSelectedConfiguration(); 2161 if (key != null) { 2162 sb.append(" ").append(key); 2163 } 2164 break; 2165 default: 2166 sb.append(" "); 2167 sb.append(Integer.toString(msg.arg1)); 2168 sb.append(" "); 2169 sb.append(Integer.toString(msg.arg2)); 2170 break; 2171 } 2172 2173 return sb.toString(); 2174 } 2175 2176 private void handleScreenStateChanged(boolean screenOn) { 2177 mScreenOn = screenOn; 2178 if (PDBG) { 2179 loge(" handleScreenStateChanged Enter: screenOn=" + screenOn 2180 + "mCurrentScanAlarmMs = " + Long.toString(mCurrentScanAlarmMs) 2181 + " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt 2182 + " autojoin " + mFrameworkAutoJoin 2183 + " state " + getCurrentState().getName() 2184 + " suppState:" + mSupplicantStateTracker.getSupplicantStateName()); 2185 } 2186 enableRssiPolling(screenOn); 2187 if (mBackgroundScanSupported) { 2188 enableBackgroundScanCommand(screenOn == false); 2189 } 2190 2191 if (screenOn) enableAllNetworks(); 2192 if (mUserWantsSuspendOpt.get()) { 2193 if (screenOn) { 2194 sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0); 2195 } else { 2196 //Allow 2s for suspend optimizations to be set 2197 mSuspendWakeLock.acquire(2000); 2198 sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0); 2199 } 2200 } 2201 mScreenBroadcastReceived.set(true); 2202 2203 if (screenOn) { 2204 fullBandConnectedTimeIntervalMilli = 20 * 1000; //start at 20 seconds interval 2205 if (mFrameworkAutoJoin.get()) { 2206 //start the scan alarm so as to enable autojoin 2207 if (getCurrentState() == mConnectedState) { 2208 mCurrentScanAlarmMs = mConnectedScanPeriodMs; 2209 } else if (getCurrentState() == mDisconnectedState) { 2210 mCurrentScanAlarmMs = mDisconnectedScanPeriodMs; 2211 //kick a scan right now 2212 startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null); 2213 } else if (getCurrentState() == mDisconnectingState) { 2214 mCurrentScanAlarmMs = mDisconnectedScanPeriodMs; 2215 //kick a scan right now 2216 startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null); 2217 } 2218 } 2219 setScanAlarm(true); 2220 2221 } else { 2222 setScanAlarm(false); 2223 } 2224 2225 if (DBG) log("handleScreenStateChanged Exit: " + screenOn); 2226 } 2227 2228 private void checkAndSetConnectivityInstance() { 2229 if (mCm == null) { 2230 mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 2231 } 2232 } 2233 2234 private boolean startTethering(ArrayList<String> available) { 2235 2236 boolean wifiAvailable = false; 2237 2238 checkAndSetConnectivityInstance(); 2239 2240 String[] wifiRegexs = mCm.getTetherableWifiRegexs(); 2241 2242 for (String intf : available) { 2243 for (String regex : wifiRegexs) { 2244 if (intf.matches(regex)) { 2245 2246 InterfaceConfiguration ifcg = null; 2247 try { 2248 ifcg = mNwService.getInterfaceConfig(intf); 2249 if (ifcg != null) { 2250 /* IP/netmask: 192.168.43.1/255.255.255.0 */ 2251 ifcg.setLinkAddress(new LinkAddress( 2252 NetworkUtils.numericToInetAddress("192.168.43.1"), 24)); 2253 ifcg.setInterfaceUp(); 2254 2255 mNwService.setInterfaceConfig(intf, ifcg); 2256 } 2257 } catch (Exception e) { 2258 loge("Error configuring interface " + intf + ", :" + e); 2259 return false; 2260 } 2261 2262 if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { 2263 loge("Error tethering on " + intf); 2264 return false; 2265 } 2266 mTetherInterfaceName = intf; 2267 return true; 2268 } 2269 } 2270 } 2271 // We found no interfaces to tether 2272 return false; 2273 } 2274 2275 private void stopTethering() { 2276 2277 checkAndSetConnectivityInstance(); 2278 2279 /* Clear the interface config to allow dhcp correctly configure new 2280 ip settings */ 2281 InterfaceConfiguration ifcg = null; 2282 try { 2283 ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName); 2284 if (ifcg != null) { 2285 ifcg.setLinkAddress( 2286 new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0)); 2287 mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg); 2288 } 2289 } catch (Exception e) { 2290 loge("Error resetting interface " + mTetherInterfaceName + ", :" + e); 2291 } 2292 2293 if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { 2294 loge("Untether initiate failed!"); 2295 } 2296 } 2297 2298 private boolean isWifiTethered(ArrayList<String> active) { 2299 2300 checkAndSetConnectivityInstance(); 2301 2302 String[] wifiRegexs = mCm.getTetherableWifiRegexs(); 2303 for (String intf : active) { 2304 for (String regex : wifiRegexs) { 2305 if (intf.matches(regex)) { 2306 return true; 2307 } 2308 } 2309 } 2310 // We found no interfaces that are tethered 2311 return false; 2312 } 2313 2314 /** 2315 * Set the country code from the system setting value, if any. 2316 */ 2317 private void setCountryCode() { 2318 String countryCode = Settings.Global.getString(mContext.getContentResolver(), 2319 Settings.Global.WIFI_COUNTRY_CODE); 2320 if (countryCode != null && !countryCode.isEmpty()) { 2321 setCountryCode(countryCode, false); 2322 } else { 2323 //use driver default 2324 } 2325 } 2326 2327 /** 2328 * Set the frequency band from the system setting value, if any. 2329 */ 2330 private void setFrequencyBand() { 2331 int band = Settings.Global.getInt(mContext.getContentResolver(), 2332 Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO); 2333 setFrequencyBand(band, false); 2334 } 2335 2336 private void setSuspendOptimizationsNative(int reason, boolean enabled) { 2337 if (DBG) { 2338 log("setSuspendOptimizationsNative: " + reason + " " + enabled 2339 + " -want " + mUserWantsSuspendOpt.get() 2340 + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 2341 +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName() 2342 +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName() 2343 +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName()); 2344 } 2345 //mWifiNative.setSuspendOptimizations(enabled); 2346 2347 if (enabled) { 2348 mSuspendOptNeedsDisabled &= ~reason; 2349 /* None of dhcp, screen or highperf need it disabled and user wants it enabled */ 2350 if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) { 2351 if (DBG) { 2352 log("setSuspendOptimizationsNative do it " + reason + " " + enabled 2353 + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 2354 +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName() 2355 +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName() 2356 +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName()); 2357 } 2358 mWifiNative.setSuspendOptimizations(true); 2359 } 2360 } else { 2361 mSuspendOptNeedsDisabled |= reason; 2362 mWifiNative.setSuspendOptimizations(false); 2363 } 2364 } 2365 2366 private void setSuspendOptimizations(int reason, boolean enabled) { 2367 if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled); 2368 if (enabled) { 2369 mSuspendOptNeedsDisabled &= ~reason; 2370 } else { 2371 mSuspendOptNeedsDisabled |= reason; 2372 } 2373 if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled); 2374 } 2375 2376 private void setWifiState(int wifiState) { 2377 final int previousWifiState = mWifiState.get(); 2378 2379 try { 2380 if (wifiState == WIFI_STATE_ENABLED) { 2381 mBatteryStats.noteWifiOn(); 2382 } else if (wifiState == WIFI_STATE_DISABLED) { 2383 mBatteryStats.noteWifiOff(); 2384 } 2385 } catch (RemoteException e) { 2386 loge("Failed to note battery stats in wifi"); 2387 } 2388 2389 mWifiState.set(wifiState); 2390 2391 if (DBG) log("setWifiState: " + syncGetWifiStateByName()); 2392 2393 final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); 2394 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 2395 intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState); 2396 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState); 2397 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 2398 } 2399 2400 private void setWifiApState(int wifiApState) { 2401 final int previousWifiApState = mWifiApState.get(); 2402 2403 try { 2404 if (wifiApState == WIFI_AP_STATE_ENABLED) { 2405 mBatteryStats.noteWifiOn(); 2406 } else if (wifiApState == WIFI_AP_STATE_DISABLED) { 2407 mBatteryStats.noteWifiOff(); 2408 } 2409 } catch (RemoteException e) { 2410 loge("Failed to note battery stats in wifi"); 2411 } 2412 2413 // Update state 2414 mWifiApState.set(wifiApState); 2415 2416 if (DBG) log("setWifiApState: " + syncGetWifiApStateByName()); 2417 2418 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 2419 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 2420 intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState); 2421 intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState); 2422 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 2423 } 2424 2425 private static final String ID_STR = "id="; 2426 private static final String BSSID_STR = "bssid="; 2427 private static final String FREQ_STR = "freq="; 2428 private static final String LEVEL_STR = "level="; 2429 private static final String TSF_STR = "tsf="; 2430 private static final String FLAGS_STR = "flags="; 2431 private static final String SSID_STR = "ssid="; 2432 private static final String DELIMITER_STR = "===="; 2433 private static final String END_STR = "####"; 2434 2435 /** 2436 * Format: 2437 * 2438 * id=1 2439 * bssid=68:7f:76:d7:1a:6e 2440 * freq=2412 2441 * level=-44 2442 * tsf=1344626243700342 2443 * flags=[WPA2-PSK-CCMP][WPS][ESS] 2444 * ssid=zfdy 2445 * ==== 2446 * id=2 2447 * bssid=68:5f:74:d7:1a:6f 2448 * freq=5180 2449 * level=-73 2450 * tsf=1344626243700373 2451 * flags=[WPA2-PSK-CCMP][WPS][ESS] 2452 * ssid=zuby 2453 * ==== 2454 */ 2455 private void setScanResults() { 2456 String bssid = ""; 2457 int level = 0; 2458 int freq = 0; 2459 long tsf = 0; 2460 String flags = ""; 2461 WifiSsid wifiSsid = null; 2462 String scanResults; 2463 String tmpResults; 2464 StringBuffer scanResultsBuf = new StringBuffer(); 2465 int sid = 0; 2466 2467 while (true) { 2468 tmpResults = mWifiNative.scanResults(sid); 2469 if (TextUtils.isEmpty(tmpResults)) break; 2470 scanResultsBuf.append(tmpResults); 2471 scanResultsBuf.append("\n"); 2472 String[] lines = tmpResults.split("\n"); 2473 sid = -1; 2474 for (int i=lines.length - 1; i >= 0; i--) { 2475 if (lines[i].startsWith(END_STR)) { 2476 break; 2477 } else if (lines[i].startsWith(ID_STR)) { 2478 try { 2479 sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1; 2480 } catch (NumberFormatException e) { 2481 // Nothing to do 2482 } 2483 break; 2484 } 2485 } 2486 if (sid == -1) break; 2487 } 2488 2489 scanResults = scanResultsBuf.toString(); 2490 if (TextUtils.isEmpty(scanResults)) { 2491 return; 2492 } 2493 2494 // note that all these splits and substrings keep references to the original 2495 // huge string buffer while the amount we really want is generally pretty small 2496 // so make copies instead (one example b/11087956 wasted 400k of heap here). 2497 synchronized(mScanResultCache) { 2498 mScanResults = new ArrayList<ScanResult>(); 2499 String[] lines = scanResults.split("\n"); 2500 final int bssidStrLen = BSSID_STR.length(); 2501 final int flagLen = FLAGS_STR.length(); 2502 2503 for (String line : lines) { 2504 if (line.startsWith(BSSID_STR)) { 2505 bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen); 2506 } else if (line.startsWith(FREQ_STR)) { 2507 try { 2508 freq = Integer.parseInt(line.substring(FREQ_STR.length())); 2509 } catch (NumberFormatException e) { 2510 freq = 0; 2511 } 2512 } else if (line.startsWith(LEVEL_STR)) { 2513 try { 2514 level = Integer.parseInt(line.substring(LEVEL_STR.length())); 2515 /* some implementations avoid negative values by adding 256 2516 * so we need to adjust for that here. 2517 */ 2518 if (level > 0) level -= 256; 2519 } catch(NumberFormatException e) { 2520 level = 0; 2521 } 2522 } else if (line.startsWith(TSF_STR)) { 2523 try { 2524 tsf = Long.parseLong(line.substring(TSF_STR.length())); 2525 } catch (NumberFormatException e) { 2526 tsf = 0; 2527 } 2528 } else if (line.startsWith(FLAGS_STR)) { 2529 flags = new String(line.getBytes(), flagLen, line.length() - flagLen); 2530 } else if (line.startsWith(SSID_STR)) { 2531 wifiSsid = WifiSsid.createFromAsciiEncoded( 2532 line.substring(SSID_STR.length())); 2533 } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) { 2534 if (bssid != null) { 2535 String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE; 2536 String key = bssid + ssid; 2537 ScanResult scanResult = mScanResultCache.get(key); 2538 if (scanResult != null) { 2539 scanResult.level = level; 2540 scanResult.wifiSsid = wifiSsid; 2541 // Keep existing API 2542 scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() : 2543 WifiSsid.NONE; 2544 scanResult.capabilities = flags; 2545 scanResult.frequency = freq; 2546 scanResult.timestamp = tsf; 2547 scanResult.seen = System.currentTimeMillis(); 2548 } else { 2549 scanResult = 2550 new ScanResult( 2551 wifiSsid, bssid, flags, level, freq, tsf); 2552 mScanResultCache.put(key, scanResult); 2553 } 2554 mScanResults.add(scanResult); 2555 } 2556 bssid = null; 2557 level = 0; 2558 freq = 0; 2559 tsf = 0; 2560 flags = ""; 2561 wifiSsid = null; 2562 } 2563 } 2564 } 2565 if (mFrameworkAutoJoin.get() == true) 2566 mWifiAutoJoinController.newSupplicantResults(); 2567 2568 } 2569 2570 /* 2571 * Fetch RSSI, linkspeed, and frequency on current connection 2572 */ 2573 private void fetchRssiLinkSpeedAndFrequencyNative() { 2574 int newRssi = -1; 2575 int newLinkSpeed = -1; 2576 int newFrequency = -1; 2577 2578 String signalPoll = mWifiNative.signalPoll(); 2579 2580 if (signalPoll != null) { 2581 String[] lines = signalPoll.split("\n"); 2582 for (String line : lines) { 2583 String[] prop = line.split("="); 2584 if (prop.length < 2) continue; 2585 try { 2586 if (prop[0].equals("RSSI")) { 2587 newRssi = Integer.parseInt(prop[1]); 2588 } else if (prop[0].equals("LINKSPEED")) { 2589 newLinkSpeed = Integer.parseInt(prop[1]); 2590 } else if (prop[0].equals("FREQUENCY")) { 2591 newFrequency = Integer.parseInt(prop[1]); 2592 } 2593 } catch (NumberFormatException e) { 2594 //Ignore, defaults on rssi and linkspeed are assigned 2595 } 2596 } 2597 } 2598 2599 if (PDBG) { 2600 loge("fetchRssiLinkSpeedAndFrequencyNative rssi=" 2601 + Integer.toString(newRssi) + " linkspeed=" 2602 + Integer.toString(newLinkSpeed)); 2603 } 2604 2605 if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) { 2606 // screen out invalid values 2607 /* some implementations avoid negative values by adding 256 2608 * so we need to adjust for that here. 2609 */ 2610 if (newRssi > 0) newRssi -= 256; 2611 mWifiInfo.setRssi(newRssi); 2612 /* 2613 * Rather then sending the raw RSSI out every time it 2614 * changes, we precalculate the signal level that would 2615 * be displayed in the status bar, and only send the 2616 * broadcast if that much more coarse-grained number 2617 * changes. This cuts down greatly on the number of 2618 * broadcasts, at the cost of not informing others 2619 * interested in RSSI of all the changes in signal 2620 * level. 2621 */ 2622 int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS); 2623 if (newSignalLevel != mLastSignalLevel) { 2624 sendRssiChangeBroadcast(newRssi); 2625 } 2626 mLastSignalLevel = newSignalLevel; 2627 } else { 2628 mWifiInfo.setRssi(WifiInfo.INVALID_RSSI); 2629 } 2630 2631 if (newLinkSpeed != -1) { 2632 mWifiInfo.setLinkSpeed(newLinkSpeed); 2633 } 2634 if (newFrequency > 0) { 2635 mWifiInfo.setFrequency(newFrequency); 2636 } 2637 } 2638 2639 /* determine if we need to switch network: 2640 * - the delta determine the urgency to switch and/or or the expected evilness of the disruption 2641 * - match the uregncy of the switch versus the packet usage at the interface 2642 */ 2643 boolean shouldSwitchNetwork(int networkDelta) { 2644 int delta; 2645 if (networkDelta <= 0) { 2646 return false; 2647 } 2648 delta = networkDelta; 2649 if (mWifiInfo != null) { 2650 //TODO: look at per AC packet count, do not switch if VO/VI traffic is present at the interface 2651 //TODO: discriminate between ucast and mcast, since the rxSuccessRate include all the bonjour and Ipv6 2652 //TODO: broadcasts 2653 if ((mWifiInfo.txSuccessRate > 20) || (mWifiInfo.rxSuccessRate > 80)) { 2654 delta -= 999; 2655 } else if ((mWifiInfo.txSuccessRate > 5) || (mWifiInfo.rxSuccessRate > 30)) { 2656 delta -= 6; 2657 } 2658 loge("WifiStateMachine shouldSwitchNetwork " 2659 + " txSuccessRate=" + String.format( "%.2f", mWifiInfo.txSuccessRate) 2660 + " rxSuccessRate=" +String.format( "%.2f", mWifiInfo.rxSuccessRate) 2661 + " delta " + networkDelta + " -> " + delta); 2662 } else { 2663 loge("WifiStateMachine shouldSwitchNetwork " 2664 + " delta " + networkDelta + " -> " + delta); 2665 } 2666 if (delta > 0) { 2667 return true; 2668 } 2669 return false; 2670 } 2671 2672 private void calculateWifiScore(WifiLinkLayerStats stats) { 2673 2674 if (stats == null || mWifiLinkLayerStatsSupported <= 0) { 2675 long mTxPkts = TrafficStats.getTxPackets(mInterfaceName); 2676 long mRxPkts = TrafficStats.getRxPackets(mInterfaceName); 2677 mWifiInfo.updatePacketRates(mTxPkts, mRxPkts); 2678 2679 } else { 2680 mWifiInfo.updatePacketRates(stats); 2681 } 2682 int score = 56; //starting score, temporarily hardcoded in between 50 and 60 2683 boolean isBadLinkspeed = (mWifiInfo.is24GHz() 2684 && mWifiInfo.getLinkSpeed() < 6) 2685 || (mWifiInfo.is5GHz() && mWifiInfo.getLinkSpeed() < 12); 2686 boolean isGoodLinkspeed = (mWifiInfo.is24GHz() 2687 && mWifiInfo.getLinkSpeed() >= 24) 2688 || (mWifiInfo.is5GHz() && mWifiInfo.getLinkSpeed() >= 48); 2689 2690 2691 //we want to make sure that we use the 24GHz RSSI thresholds is 2692 //there are 2.4GHz scan results 2693 //otherwise we end up lowering the score based on 5GHz values 2694 //which may cause a switch to LTE before roaming has a chance to try 2.4GHz 2695 //We also might unblacklist the configuation based on 2.4GHz 2696 //thresholds but joining 5GHz anyhow, and failing over to 2.4GHz because 5GHz is not good 2697 boolean use24Thresholds = false; 2698 boolean homeNetworkBoost = false; 2699 WifiConfiguration currentConfiguration = getCurrentWifiConfiguration(); 2700 if (currentConfiguration != null 2701 && currentConfiguration.scanResultCache != null) { 2702 currentConfiguration.setVisibility(12000); 2703 if (currentConfiguration.visibility != null) { 2704 if (currentConfiguration.visibility.rssi24 != WifiConfiguration.INVALID_RSSI 2705 && currentConfiguration.visibility.rssi24 2706 >= (currentConfiguration.visibility.rssi5-2)) { 2707 use24Thresholds = true; 2708 } 2709 } 2710 if (currentConfiguration.scanResultCache.size() <= 4 2711 && currentConfiguration.allowedKeyManagement.cardinality() == 1 2712 && currentConfiguration.allowedKeyManagement. 2713 get(WifiConfiguration.KeyMgmt.WPA_PSK) == true) { 2714 //a PSK network with less than 4 known BSSIDs 2715 //This is most likely a home network and thus we want to stick to wifi more 2716 homeNetworkBoost = true; 2717 } 2718 } 2719 2720 int rssi = mWifiInfo.getRssi() - 6 * mAggressiveHandover 2721 + (homeNetworkBoost ? WifiConfiguration.HOME_NETWORK_RSSI_BOOST : 0); 2722 boolean is24GHz = use24Thresholds || mWifiInfo.is24GHz(); 2723 2724 boolean isBadRSSI = (is24GHz && rssi < WifiConfiguration.BAD_RSSI_24 ) 2725 || (!is24GHz && rssi < WifiConfiguration.BAD_RSSI_5); 2726 boolean isLowRSSI = (is24GHz && rssi < WifiConfiguration.LOW_RSSI_24) 2727 || (!is24GHz && mWifiInfo.getRssi() < WifiConfiguration.LOW_RSSI_5); 2728 boolean isHighRSSI = (is24GHz && rssi >= WifiConfiguration.GOOD_RSSI_24) 2729 || (!is24GHz && mWifiInfo.getRssi() >= WifiConfiguration.GOOD_RSSI_5); 2730 2731 if (PDBG) { 2732 String rssiStatus = ""; 2733 if (isBadRSSI) rssiStatus += " badRSSI "; 2734 else if (isHighRSSI) rssiStatus += " highRSSI "; 2735 else if (isLowRSSI) rssiStatus += " lowRSSI "; 2736 if (isBadLinkspeed) rssiStatus += " lowSpeed "; 2737 loge("calculateWifiScore freq=" + Integer.toString(mWifiInfo.getFrequency()) 2738 + " speed=" + Integer.toString(mWifiInfo.getLinkSpeed()) 2739 + " score=" + Integer.toString(mWifiInfo.score) 2740 + rssiStatus 2741 + " -> txbadrate=" + String.format( "%.2f", mWifiInfo.txBadRate ) 2742 + " txgoodrate=" + String.format("%.2f", mWifiInfo.txSuccessRate) 2743 + " txretriesrate=" + String.format("%.2f", mWifiInfo.txRetriesRate) 2744 + " rxrate=" + String.format("%.2f", mWifiInfo.rxSuccessRate) 2745 ); 2746 } 2747 2748 if ((mWifiInfo.txBadRate >= 1) && (mWifiInfo.txSuccessRate < 3) 2749 && (isBadRSSI || isLowRSSI)) { 2750 //link is stuck 2751 if (mWifiInfo.linkStuckCount < 5) 2752 mWifiInfo.linkStuckCount += 1; 2753 if (PDBG) loge(" bad link -> stuck count =" 2754 + Integer.toString(mWifiInfo.linkStuckCount)); 2755 } else if (mWifiInfo.txSuccessRate > 2 || mWifiInfo.txBadRate < 0.1) { 2756 if (mWifiInfo.linkStuckCount > 0) 2757 mWifiInfo.linkStuckCount -= 1; 2758 if (PDBG) loge(" good link -> stuck count =" 2759 + Integer.toString(mWifiInfo.linkStuckCount)); 2760 } 2761 2762 if (mWifiInfo.linkStuckCount > 1) { 2763 //once link gets stuck for more than 3 seconds, start reducing the score 2764 score = score - 2 * (mWifiInfo.linkStuckCount - 1); 2765 } 2766 2767 if (isBadLinkspeed) { 2768 score -= 4; 2769 if (PDBG) loge(" isBadLinkspeed ---> score=" + Integer.toString(score)); 2770 } else if ((isGoodLinkspeed) && (mWifiInfo.txSuccessRate > 5)) { 2771 score += 4; //so as bad rssi alone dont kill us 2772 } 2773 2774 if (isBadRSSI) { 2775 if (mWifiInfo.badRssiCount < 7) 2776 mWifiInfo.badRssiCount += 1; 2777 } else if (isLowRSSI) { 2778 mWifiInfo.lowRssiCount = 1; //dont increment 2779 if (mWifiInfo.badRssiCount > 0) { 2780 mWifiInfo.badRssiCount -= 1; 2781 } 2782 } else { 2783 mWifiInfo.badRssiCount = 0; 2784 mWifiInfo.lowRssiCount = 0; 2785 } 2786 2787 score -= mWifiInfo.badRssiCount * 2 + mWifiInfo.lowRssiCount ; 2788 2789 if (PDBG) loge(" badRSSI count" + Integer.toString(mWifiInfo.badRssiCount) 2790 + " lowRSSI count" + Integer.toString(mWifiInfo.lowRssiCount) 2791 + " --> score " + Integer.toString(score)); 2792 2793 2794 if (isHighRSSI) { 2795 score += 5; 2796 if (PDBG) loge(" isHighRSSI ---> score=" + Integer.toString(score)); 2797 } 2798 2799 //sanitize boundaries 2800 if (score > NetworkAgent.WIFI_BASE_SCORE) 2801 score = NetworkAgent.WIFI_BASE_SCORE; 2802 if (score < 0) 2803 score = 0; 2804 2805 //report score 2806 if (score != mWifiInfo.score) { 2807 if (DBG) { 2808 loge("calculateWifiScore() report new score " + Integer.toString(score)); 2809 } 2810 mWifiInfo.score = score; 2811 mNetworkAgent.sendNetworkScore(score); 2812 } 2813 } 2814 2815 public double getTxPacketRate() { 2816 if (mWifiInfo != null) { 2817 return mWifiInfo.txSuccessRate; 2818 } 2819 return -1; 2820 } 2821 2822 public double getRxPacketRate() { 2823 if (mWifiInfo != null) { 2824 return mWifiInfo.rxSuccessRate; 2825 } 2826 return -1; 2827 } 2828 2829 /* 2830 * Fetch TX packet counters on current connection 2831 */ 2832 private void fetchPktcntNative(RssiPacketCountInfo info) { 2833 String pktcntPoll = mWifiNative.pktcntPoll(); 2834 2835 if (pktcntPoll != null) { 2836 String[] lines = pktcntPoll.split("\n"); 2837 for (String line : lines) { 2838 String[] prop = line.split("="); 2839 if (prop.length < 2) continue; 2840 try { 2841 if (prop[0].equals("TXGOOD")) { 2842 info.txgood = Integer.parseInt(prop[1]); 2843 } else if (prop[0].equals("TXBAD")) { 2844 info.txbad = Integer.parseInt(prop[1]); 2845 } 2846 } catch (NumberFormatException e) { 2847 //Ignore 2848 } 2849 } 2850 } 2851 } 2852 2853 /** 2854 * Updates mLinkProperties by merging information from various sources. 2855 * 2856 * This is needed because the information in mLinkProperties comes from multiple sources (DHCP, 2857 * netlink, static configuration, ...). When one of these sources of information has updated 2858 * link properties, we can't just assign them to mLinkProperties or we'd lose track of the 2859 * information that came from other sources. Instead, when one of those sources has new 2860 * information, we update the object that tracks the information from that source and then 2861 * call this method to apply the change to mLinkProperties. 2862 * 2863 * The information in mLinkProperties is currently obtained as follows: 2864 * - Interface name: set in the constructor. 2865 * - IPv4 and IPv6 addresses: netlink, passed in by mNetlinkTracker. 2866 * - IPv4 routes, DNS servers, and domains: DHCP. 2867 * - IPv6 routes and DNS servers: netlink, passed in by mNetlinkTracker. 2868 * - HTTP proxy: the wifi config store. 2869 */ 2870 private void updateLinkProperties() { 2871 LinkProperties newLp = new LinkProperties(); 2872 2873 // Interface name and proxy are locally configured. 2874 newLp.setInterfaceName(mInterfaceName); 2875 newLp.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId)); 2876 2877 // IPv4/v6 addresses, IPv6 routes and IPv6 DNS servers come from netlink. 2878 LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties(); 2879 newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses()); 2880 for (RouteInfo route : netlinkLinkProperties.getRoutes()) { 2881 newLp.addRoute(route); 2882 } 2883 for (InetAddress dns : netlinkLinkProperties.getDnsServers()) { 2884 newLp.addDnsServer(dns); 2885 } 2886 2887 // IPv4 routes, DNS servers and domains come from mDhcpResults. 2888 synchronized (mDhcpResultsLock) { 2889 // Even when we're using static configuration, we don't need to look at the config 2890 // store, because static IP configuration also populates mDhcpResults. 2891 if ((mDhcpResults != null) && (mDhcpResults.linkProperties != null)) { 2892 LinkProperties lp = mDhcpResults.linkProperties; 2893 for (RouteInfo route : lp.getRoutes()) { 2894 newLp.addRoute(route); 2895 } 2896 for (InetAddress dns : lp.getDnsServers()) { 2897 newLp.addDnsServer(dns); 2898 } 2899 newLp.setDomains(lp.getDomains()); 2900 } 2901 } 2902 2903 if (!newLp.equals(mLinkProperties)) { 2904 if (DBG) { 2905 log("Link configuration changed for netId: " + mLastNetworkId 2906 + " old: " + mLinkProperties + "new: " + newLp); 2907 } 2908 2909 boolean wasProvisioned = mLinkProperties.isProvisioned(); 2910 boolean isProvisioned = newLp.isProvisioned(); 2911 2912 mLinkProperties = newLp; 2913 if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties); 2914 2915 // If we just configured or lost IP configuration, do the needful. 2916 // We don't just call handleSuccessfulIpConfiguration() or handleIpConfigurationLost() 2917 // here because those should only be called if we're attempting to connect or already 2918 // connected, whereas updateLinkProperties can be called at any time. 2919 if (!wasProvisioned && isProvisioned) { 2920 sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL); 2921 } else if (wasProvisioned && !isProvisioned) { 2922 sendMessage(CMD_IP_CONFIGURATION_LOST); 2923 } else if (getNetworkDetailedState() == DetailedState.CONNECTED) { 2924 // If anything has changed, and we're already connected, send out a notification. 2925 sendLinkConfigurationChangedBroadcast(); 2926 } 2927 2928 } 2929 } 2930 2931 /** 2932 * Clears all our link properties. 2933 */ 2934 private void clearLinkProperties() { 2935 // If the network used DHCP, clear the LinkProperties we stored in the config store. 2936 if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) { 2937 mWifiConfigStore.clearLinkProperties(mLastNetworkId); 2938 } 2939 2940 // Clear the link properties obtained from DHCP and netlink. 2941 synchronized (mDhcpResultsLock) { 2942 if (mDhcpResults != null && mDhcpResults.linkProperties != null) { 2943 mDhcpResults.linkProperties.clear(); 2944 } 2945 } 2946 mNetlinkTracker.clearLinkProperties(); 2947 2948 // Now clear the merged link properties. 2949 mLinkProperties.clear(); 2950 if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties); 2951 } 2952 2953 /** 2954 * try to update default route MAC address. 2955 */ 2956 private String updateDefaultRouteMacAddress(int timeout) { 2957 String address = null; 2958 for (RouteInfo route : mLinkProperties.getRoutes()) { 2959 if (route.isDefaultRoute() && route.hasGateway()) { 2960 InetAddress gateway = route.getGateway(); 2961 if (gateway instanceof Inet4Address) { 2962 if (PDBG) { 2963 loge("updateDefaultRouteMacAddress found Ipv4 default :" 2964 + gateway.getHostAddress()); 2965 } 2966 address = macAddressFromRoute(gateway.getHostAddress()); 2967 /* the gateway's MAC address is known */ 2968 if ((address == null) && (timeout > 0)) { 2969 boolean reachable = false; 2970 try { 2971 reachable = gateway.isReachable(timeout); 2972 } catch (Exception e) { 2973 loge("updateDefaultRouteMacAddress exception reaching :" 2974 + gateway.getHostAddress()); 2975 2976 } finally { 2977 if (reachable == true) { 2978 2979 address = macAddressFromRoute(gateway.getHostAddress()); 2980 if (PDBG) { 2981 loge("updateDefaultRouteMacAddress reachable (tried again) :" 2982 + gateway.getHostAddress() + " found " + address); 2983 } 2984 } 2985 } 2986 } 2987 if (address != null) { 2988 mWifiConfigStore.setLinkProperties(mLastNetworkId, 2989 new LinkProperties(mLinkProperties)); 2990 mWifiConfigStore.setDefaultGwMacAddress(mLastNetworkId, address); 2991 2992 } 2993 } 2994 } 2995 } 2996 return address; 2997 } 2998 2999 private int getMaxDhcpRetries() { 3000 return Settings.Global.getInt(mContext.getContentResolver(), 3001 Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, 3002 DEFAULT_MAX_DHCP_RETRIES); 3003 } 3004 3005 private void sendScanResultsAvailableBroadcast() { 3006 noteScanEnd(); 3007 Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 3008 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 3009 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 3010 } 3011 3012 private void sendRssiChangeBroadcast(final int newRssi) { 3013 try { 3014 mBatteryStats.noteWifiRssiChanged(newRssi); 3015 } catch (RemoteException e) { 3016 // Won't happen. 3017 } 3018 Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION); 3019 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 3020 intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi); 3021 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 3022 } 3023 3024 private void sendNetworkStateChangeBroadcast(String bssid) { 3025 Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); 3026 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 3027 intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo)); 3028 intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties)); 3029 if (bssid != null) 3030 intent.putExtra(WifiManager.EXTRA_BSSID, bssid); 3031 if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK || 3032 mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) { 3033 intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo)); 3034 } 3035 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 3036 } 3037 3038 private void sendLinkConfigurationChangedBroadcast() { 3039 Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); 3040 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 3041 intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties)); 3042 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 3043 } 3044 3045 private void sendSupplicantConnectionChangedBroadcast(boolean connected) { 3046 Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); 3047 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 3048 intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected); 3049 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 3050 } 3051 3052 /** 3053 * Record the detailed state of a network. 3054 * @param state the new {@code DetailedState} 3055 */ 3056 private void setNetworkDetailedState(NetworkInfo.DetailedState state) { 3057 if (DBG) { 3058 log("setDetailed state, old =" 3059 + mNetworkInfo.getDetailedState() + " and new state=" + state); 3060 } 3061 3062 if (state != mNetworkInfo.getDetailedState()) { 3063 mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID()); 3064 if (mNetworkAgent != null) { 3065 if (!isRoaming() || 3066 (state != DetailedState.DISCONNECTED && state != DetailedState.DISCONNECTING) ) { 3067 //don't tell the Network agent if we are doing a disconnect-roam 3068 mNetworkAgent.sendNetworkInfo(mNetworkInfo); 3069 } 3070 } 3071 } 3072 } 3073 3074 private DetailedState getNetworkDetailedState() { 3075 return mNetworkInfo.getDetailedState(); 3076 } 3077 3078 3079 private SupplicantState handleSupplicantStateChange(Message message) { 3080 StateChangeResult stateChangeResult = (StateChangeResult) message.obj; 3081 SupplicantState state = stateChangeResult.state; 3082 // Supplicant state change 3083 // [31-13] Reserved for future use 3084 // [8 - 0] Supplicant state (as defined in SupplicantState.java) 3085 // 50023 supplicant_state_changed (custom|1|5) 3086 mWifiInfo.setSupplicantState(state); 3087 // Network id is only valid when we start connecting 3088 if (SupplicantState.isConnecting(state)) { 3089 mWifiInfo.setNetworkId(stateChangeResult.networkId); 3090 } else { 3091 mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); 3092 } 3093 3094 mWifiInfo.setBSSID(stateChangeResult.BSSID); 3095 mWifiInfo.setSSID(stateChangeResult.wifiSsid); 3096 3097 mSupplicantStateTracker.sendMessage(Message.obtain(message)); 3098 3099 return state; 3100 } 3101 3102 /** 3103 * Resets the Wi-Fi Connections by clearing any state, resetting any sockets 3104 * using the interface, stopping DHCP & disabling interface 3105 */ 3106 private void handleNetworkDisconnect() { 3107 if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP" 3108 + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() 3109 +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName() 3110 +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName() 3111 +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName()); 3112 3113 stopDhcp(); 3114 3115 try { 3116 mNwService.clearInterfaceAddresses(mInterfaceName); 3117 mNwService.disableIpv6(mInterfaceName); 3118 } catch (Exception e) { 3119 loge("Failed to clear addresses or disable ipv6" + e); 3120 } 3121 3122 /* Reset data structures */ 3123 mWifiInfo.reset(); 3124 3125 setNetworkDetailedState(DetailedState.DISCONNECTED); 3126 if (mNetworkAgent != null) { 3127 mNetworkAgent.sendNetworkInfo(mNetworkInfo); 3128 mNetworkAgent = null; 3129 } 3130 mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED); 3131 3132 /* Clear network properties */ 3133 clearLinkProperties(); 3134 3135 /* send event to CM & network change broadcast */ 3136 sendNetworkStateChangeBroadcast(mLastBssid); 3137 3138 //cancel auto roam requests 3139 autoRoamSetBSSID(mLastNetworkId, "any"); 3140 3141 //reset roaming parameters 3142 mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE; 3143 fullBandConnectedTimeIntervalMilli = 20 * 1000; //start at 20 seconds interval 3144 3145 mLastBssid= null; 3146 registerDisconnected(); 3147 mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; 3148 } 3149 3150 private void handleSupplicantConnectionLoss() { 3151 /* Socket connection can be lost when we do a graceful shutdown 3152 * or when the driver is hung. Ensure supplicant is stopped here. 3153 */ 3154 mWifiMonitor.killSupplicant(mP2pSupported); 3155 mWifiNative.closeSupplicantConnection(); 3156 sendSupplicantConnectionChangedBroadcast(false); 3157 setWifiState(WIFI_STATE_DISABLED); 3158 } 3159 3160 void handlePreDhcpSetup() { 3161 mDhcpActive = true; 3162 if (!mBluetoothConnectionActive) { 3163 /* 3164 * There are problems setting the Wi-Fi driver's power 3165 * mode to active when bluetooth coexistence mode is 3166 * enabled or sense. 3167 * <p> 3168 * We set Wi-Fi to active mode when 3169 * obtaining an IP address because we've found 3170 * compatibility issues with some routers with low power 3171 * mode. 3172 * <p> 3173 * In order for this active power mode to properly be set, 3174 * we disable coexistence mode until we're done with 3175 * obtaining an IP address. One exception is if we 3176 * are currently connected to a headset, since disabling 3177 * coexistence would interrupt that connection. 3178 */ 3179 // Disable the coexistence mode 3180 mWifiNative.setBluetoothCoexistenceMode( 3181 mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); 3182 } 3183 3184 /* Disable power save and suspend optimizations during DHCP */ 3185 // Note: The order here is important for now. Brcm driver changes 3186 // power settings when we control suspend mode optimizations. 3187 // TODO: Remove this comment when the driver is fixed. 3188 setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false); 3189 mWifiNative.setPowerSave(false); 3190 3191 stopBatchedScan(); 3192 WifiNative.pauseScan(); 3193 3194 /* P2p discovery breaks dhcp, shut it down in order to get through this */ 3195 Message msg = new Message(); 3196 msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY; 3197 msg.arg1 = WifiP2pServiceImpl.ENABLED; 3198 msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE; 3199 msg.obj = mDhcpStateMachine; 3200 mWifiP2pChannel.sendMessage(msg); 3201 } 3202 3203 3204 void startDhcp() { 3205 if (mDhcpStateMachine == null) { 3206 mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine( 3207 mContext, WifiStateMachine.this, mInterfaceName); 3208 3209 } 3210 mDhcpStateMachine.registerForPreDhcpNotification(); 3211 mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP); 3212 } 3213 3214 void renewDhcp() { 3215 if (mDhcpStateMachine == null) { 3216 mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine( 3217 mContext, WifiStateMachine.this, mInterfaceName); 3218 3219 } 3220 mDhcpStateMachine.registerForPreDhcpNotification(); 3221 mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_RENEW_DHCP); 3222 } 3223 3224 void stopDhcp() { 3225 if (mDhcpStateMachine != null) { 3226 /* In case we were in middle of DHCP operation restore back powermode */ 3227 handlePostDhcpSetup(); 3228 mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP); 3229 } 3230 } 3231 3232 void handlePostDhcpSetup() { 3233 /* Restore power save and suspend optimizations */ 3234 setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true); 3235 mWifiNative.setPowerSave(true); 3236 3237 mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED); 3238 3239 // Set the coexistence mode back to its default value 3240 mWifiNative.setBluetoothCoexistenceMode( 3241 mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); 3242 3243 mDhcpActive = false; 3244 3245 startBatchedScan(); 3246 WifiNative.restartScan(); 3247 } 3248 3249 private void handleIPv4Success(DhcpResults dhcpResults) { 3250 3251 if (PDBG) { 3252 loge("handleIPv4Success <" + dhcpResults.toString() 3253 + "> linkaddress num " + dhcpResults.linkProperties.getLinkAddresses().size()); 3254 for (LinkAddress linkAddress : dhcpResults.linkProperties.getLinkAddresses()) { 3255 loge("link address " + linkAddress.toString()); 3256 } 3257 } 3258 3259 synchronized (mDhcpResultsLock) { 3260 mDhcpResults = dhcpResults; 3261 } 3262 LinkProperties linkProperties = dhcpResults.linkProperties; 3263 mWifiConfigStore.setLinkProperties(mLastNetworkId, new LinkProperties(linkProperties)); 3264 InetAddress addr = null; 3265 Iterator<InetAddress> addrs = linkProperties.getAddresses().iterator(); 3266 if (addrs.hasNext()) { 3267 addr = addrs.next(); 3268 } 3269 3270 if (isRoaming()) { 3271 if (addr instanceof Inet4Address) { 3272 int previousAddress = mWifiInfo.getIpAddress(); 3273 int newAddress = NetworkUtils.inetAddressToInt((Inet4Address)addr); 3274 if (previousAddress != newAddress) { 3275 loge("handleIPv4Success, roaming and address changed" + 3276 mWifiInfo + " got: " + addr); 3277 } else { 3278 3279 } 3280 } else { 3281 loge("handleIPv4Success, roaming and didnt get an IPv4 address" + 3282 addr.toString()); 3283 3284 3285 } 3286 } 3287 mWifiInfo.setInetAddress(addr); 3288 mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint()); 3289 updateLinkProperties(); 3290 } 3291 3292 private void handleSuccessfulIpConfiguration() { 3293 mLastSignalLevel = -1; // force update of signal strength 3294 mReconnectCount = 0; //Reset IP failure tracking 3295 } 3296 3297 private void handleIPv4Failure() { 3298 synchronized(mDhcpResultsLock) { 3299 if (mDhcpResults != null && mDhcpResults.linkProperties != null) { 3300 mDhcpResults.linkProperties.clear(); 3301 } 3302 } 3303 updateLinkProperties(); 3304 } 3305 3306 private void handleIpConfigurationLost() { 3307 mWifiInfo.setInetAddress(null); 3308 mWifiInfo.setMeteredHint(false); 3309 /** 3310 * If we've exceeded the maximum number of retries for DHCP 3311 * to a given network, disable the network 3312 */ 3313 int maxRetries = getMaxDhcpRetries(); 3314 // maxRetries == 0 means keep trying forever 3315 if (maxRetries > 0 && ++mReconnectCount > maxRetries) { 3316 loge("Failed " + 3317 mReconnectCount + " times, Disabling " + mLastNetworkId); 3318 mWifiConfigStore.disableNetwork(mLastNetworkId, 3319 WifiConfiguration.DISABLED_DHCP_FAILURE); 3320 mReconnectCount = 0; 3321 } 3322 3323 /* DHCP times out after about 30 seconds, we do a 3324 * disconnect and an immediate reconnect to try again 3325 */ 3326 mWifiNative.disconnect(); 3327 mWifiNative.reconnect(); 3328 } 3329 3330 /* Current design is to not set the config on a running hostapd but instead 3331 * stop and start tethering when user changes config on a running access point 3332 * 3333 * TODO: Add control channel setup through hostapd that allows changing config 3334 * on a running daemon 3335 */ 3336 private void startSoftApWithConfig(final WifiConfiguration config) { 3337 // start hostapd on a seperate thread 3338 new Thread(new Runnable() { 3339 public void run() { 3340 try { 3341 mNwService.startAccessPoint(config, mInterfaceName); 3342 } catch (Exception e) { 3343 loge("Exception in softap start " + e); 3344 try { 3345 mNwService.stopAccessPoint(mInterfaceName); 3346 mNwService.startAccessPoint(config, mInterfaceName); 3347 } catch (Exception e1) { 3348 loge("Exception in softap re-start " + e1); 3349 sendMessage(CMD_START_AP_FAILURE); 3350 return; 3351 } 3352 } 3353 if (DBG) log("Soft AP start successful"); 3354 sendMessage(CMD_START_AP_SUCCESS); 3355 } 3356 }).start(); 3357 } 3358 3359 3360 /* 3361 * Read a MAC address in /proc/arp/table, used by WifistateMachine 3362 * so as to record MAC address of default gateway. 3363 **/ 3364 private String macAddressFromRoute(String ipAddress) { 3365 String macAddress = null; 3366 BufferedReader reader = null; 3367 try { 3368 reader = new BufferedReader(new FileReader("/proc/net/arp")); 3369 3370 // Skip over the line bearing colum titles 3371 String line = reader.readLine(); 3372 3373 while ((line = reader.readLine()) != null) { 3374 String[] tokens = line.split("[ ]+"); 3375 if (tokens.length < 6) { 3376 continue; 3377 } 3378 3379 // ARP column format is 3380 // Address HWType HWAddress Flags Mask IFace 3381 String ip = tokens[0]; 3382 String mac = tokens[3]; 3383 3384 if (ipAddress.equals(ip)) { 3385 macAddress = mac; 3386 break; 3387 } 3388 } 3389 3390 if (macAddress == null) { 3391 loge("Did not find remoteAddress {" + ipAddress + "} in " + 3392 "/proc/net/arp"); 3393 } 3394 3395 } catch (FileNotFoundException e) { 3396 loge("Could not open /proc/net/arp to lookup mac address"); 3397 } catch (IOException e) { 3398 loge("Could not read /proc/net/arp to lookup mac address"); 3399 } finally { 3400 try { 3401 if (reader != null) { 3402 reader.close(); 3403 } 3404 } catch (IOException e) { 3405 // Do nothing 3406 } 3407 } 3408 return macAddress; 3409 3410 } 3411 3412 private class WifiNetworkFactory extends NetworkFactory { 3413 public WifiNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities f) { 3414 super(l, c, TAG, f); 3415 } 3416 protected void startNetwork() { 3417 // TODO 3418 // enter association mode. 3419 } 3420 protected void stopNetwork() { 3421 // TODO 3422 // stop associating. 3423 } 3424 } 3425 /******************************************************** 3426 * HSM states 3427 *******************************************************/ 3428 3429 class DefaultState extends State { 3430 @Override 3431 public boolean processMessage(Message message) { 3432 logStateAndMessage(message, getClass().getSimpleName()); 3433 3434 switch (message.what) { 3435 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { 3436 AsyncChannel ac = (AsyncChannel) message.obj; 3437 if (ac == mWifiP2pChannel) { 3438 if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 3439 mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); 3440 } else { 3441 loge("WifiP2pService connection failure, error=" + message.arg1); 3442 } 3443 } else { 3444 loge("got HALF_CONNECTED for unknown channel"); 3445 } 3446 break; 3447 } 3448 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { 3449 AsyncChannel ac = (AsyncChannel) message.obj; 3450 if (ac == mWifiP2pChannel) { 3451 loge("WifiP2pService channel lost, message.arg1 =" + message.arg1); 3452 //TODO: Re-establish connection to state machine after a delay 3453 //mWifiP2pChannel.connect(mContext, getHandler(), 3454 // mWifiP2pManager.getMessenger()); 3455 } 3456 break; 3457 } 3458 case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE: 3459 mBluetoothConnectionActive = (message.arg1 != 3460 BluetoothAdapter.STATE_DISCONNECTED); 3461 break; 3462 /* Synchronous call returns */ 3463 case CMD_PING_SUPPLICANT: 3464 case CMD_ENABLE_NETWORK: 3465 case CMD_ADD_OR_UPDATE_NETWORK: 3466 case CMD_REMOVE_NETWORK: 3467 case CMD_SAVE_CONFIG: 3468 replyToMessage(message, message.what, FAILURE); 3469 break; 3470 case CMD_GET_CAPABILITY_FREQ: 3471 replyToMessage(message, message.what, null); 3472 break; 3473 case CMD_GET_CONFIGURED_NETWORKS: 3474 replyToMessage(message, message.what, (List<WifiConfiguration>) null); 3475 break; 3476 case CMD_ENABLE_RSSI_POLL: 3477 mEnableRssiPolling = (message.arg1 == 1); 3478 break; 3479 case CMD_ENABLE_BACKGROUND_SCAN: 3480 mEnableBackgroundScan = (message.arg1 == 1); 3481 break; 3482 case CMD_SET_HIGH_PERF_MODE: 3483 if (message.arg1 == 1) { 3484 setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false); 3485 } else { 3486 setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true); 3487 } 3488 break; 3489 case CMD_BOOT_COMPLETED: 3490 String countryCode = mPersistedCountryCode; 3491 if (TextUtils.isEmpty(countryCode) == false) { 3492 Settings.Global.putString(mContext.getContentResolver(), 3493 Settings.Global.WIFI_COUNTRY_CODE, 3494 countryCode); 3495 // it may be that the state transition that should send this info 3496 // to the driver happened between mPersistedCountryCode getting set 3497 // and now, so simply persisting it here would mean we have sent 3498 // nothing to the driver. Send the cmd so it might be set now. 3499 int sequenceNum = mCountryCodeSequence.incrementAndGet(); 3500 sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE, 3501 sequenceNum, 0, countryCode); 3502 } 3503 3504 checkAndSetConnectivityInstance(); 3505 mNetworkFactory = new WifiNetworkFactory(getHandler().getLooper(), mContext, 3506 NETWORKTYPE, mNetworkCapabilitiesFilter); 3507 mNetworkFactory.setScoreFilter(60); 3508 mCm.registerNetworkFactory(new Messenger(mNetworkFactory), NETWORKTYPE); 3509 break; 3510 case CMD_SET_BATCHED_SCAN: 3511 recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj); 3512 break; 3513 case CMD_POLL_BATCHED_SCAN: 3514 handleBatchedScanPollRequest(); 3515 break; 3516 case CMD_START_NEXT_BATCHED_SCAN: 3517 startNextBatchedScan(); 3518 break; 3519 /* Discard */ 3520 case CMD_START_SCAN: 3521 case CMD_START_SUPPLICANT: 3522 case CMD_STOP_SUPPLICANT: 3523 case CMD_STOP_SUPPLICANT_FAILED: 3524 case CMD_START_DRIVER: 3525 case CMD_STOP_DRIVER: 3526 case CMD_DELAYED_STOP_DRIVER: 3527 case CMD_DRIVER_START_TIMED_OUT: 3528 case CMD_START_AP: 3529 case CMD_START_AP_SUCCESS: 3530 case CMD_START_AP_FAILURE: 3531 case CMD_STOP_AP: 3532 case CMD_TETHER_STATE_CHANGE: 3533 case CMD_TETHER_NOTIFICATION_TIMED_OUT: 3534 case CMD_DISCONNECT: 3535 case CMD_RECONNECT: 3536 case CMD_REASSOCIATE: 3537 case CMD_RELOAD_TLS_AND_RECONNECT: 3538 case WifiMonitor.SUP_CONNECTION_EVENT: 3539 case WifiMonitor.SUP_DISCONNECTION_EVENT: 3540 case WifiMonitor.NETWORK_CONNECTION_EVENT: 3541 case WifiMonitor.NETWORK_DISCONNECTION_EVENT: 3542 case WifiMonitor.SCAN_RESULTS_EVENT: 3543 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 3544 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: 3545 case WifiMonitor.ASSOCIATION_REJECTION_EVENT: 3546 case WifiMonitor.WPS_OVERLAP_EVENT: 3547 case CMD_BLACKLIST_NETWORK: 3548 case CMD_CLEAR_BLACKLIST: 3549 case CMD_SET_OPERATIONAL_MODE: 3550 case CMD_SET_COUNTRY_CODE: 3551 case CMD_SET_FREQUENCY_BAND: 3552 case CMD_RSSI_POLL: 3553 case CMD_ENABLE_ALL_NETWORKS: 3554 case DhcpStateMachine.CMD_PRE_DHCP_ACTION: 3555 case DhcpStateMachine.CMD_POST_DHCP_ACTION: 3556 /* Handled by WifiApConfigStore */ 3557 case CMD_SET_AP_CONFIG: 3558 case CMD_SET_AP_CONFIG_COMPLETED: 3559 case CMD_REQUEST_AP_CONFIG: 3560 case CMD_RESPONSE_AP_CONFIG: 3561 case WifiWatchdogStateMachine.POOR_LINK_DETECTED: 3562 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED: 3563 case CMD_NO_NETWORKS_PERIODIC_SCAN: 3564 case CMD_DISABLE_P2P_RSP: 3565 case WifiMonitor.SUP_REQUEST_IDENTITY: 3566 break; 3567 case DhcpStateMachine.CMD_ON_QUIT: 3568 mDhcpStateMachine = null; 3569 break; 3570 case CMD_SET_SUSPEND_OPT_ENABLED: 3571 if (message.arg1 == 1) { 3572 mSuspendWakeLock.release(); 3573 setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true); 3574 } else { 3575 setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false); 3576 } 3577 break; 3578 case WifiMonitor.DRIVER_HUNG_EVENT: 3579 setSupplicantRunning(false); 3580 setSupplicantRunning(true); 3581 break; 3582 case WifiManager.CONNECT_NETWORK: 3583 replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, 3584 WifiManager.BUSY); 3585 break; 3586 case WifiManager.FORGET_NETWORK: 3587 replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED, 3588 WifiManager.BUSY); 3589 break; 3590 case WifiManager.SAVE_NETWORK: 3591 replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, 3592 WifiManager.BUSY); 3593 break; 3594 case WifiManager.START_WPS: 3595 replyToMessage(message, WifiManager.WPS_FAILED, 3596 WifiManager.BUSY); 3597 break; 3598 case WifiManager.CANCEL_WPS: 3599 replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, 3600 WifiManager.BUSY); 3601 break; 3602 case WifiManager.DISABLE_NETWORK: 3603 replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED, 3604 WifiManager.BUSY); 3605 break; 3606 case WifiManager.RSSI_PKTCNT_FETCH: 3607 replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED, 3608 WifiManager.BUSY); 3609 break; 3610 case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED: 3611 NetworkInfo info = (NetworkInfo) message.obj; 3612 mP2pConnected.set(info.isConnected()); 3613 break; 3614 case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST: 3615 mTemporarilyDisconnectWifi = (message.arg1 == 1); 3616 replyToMessage(message, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE); 3617 break; 3618 /* Link configuration (IP address, DNS, ...) changes notified via netlink */ 3619 case CMD_NETLINK_UPDATE: 3620 updateLinkProperties(); 3621 break; 3622 case CMD_IP_CONFIGURATION_SUCCESSFUL: 3623 case CMD_IP_CONFIGURATION_LOST: 3624 break; 3625 default: 3626 loge("Error! unhandled message" + message); 3627 break; 3628 } 3629 return HANDLED; 3630 } 3631 } 3632 3633 class InitialState extends State { 3634 @Override 3635 public void enter() { 3636 mWifiNative.unloadDriver(); 3637 3638 if (mWifiP2pChannel == null) { 3639 mWifiP2pChannel = new AsyncChannel(); 3640 mWifiP2pChannel.connect(mContext, getHandler(), 3641 mWifiP2pServiceImpl.getP2pStateMachineMessenger()); 3642 } 3643 3644 if (mWifiApConfigChannel == null) { 3645 mWifiApConfigChannel = new AsyncChannel(); 3646 WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore( 3647 mContext, getHandler()); 3648 wifiApConfigStore.loadApConfiguration(); 3649 mWifiApConfigChannel.connectSync(mContext, getHandler(), 3650 wifiApConfigStore.getMessenger()); 3651 } 3652 } 3653 @Override 3654 public boolean processMessage(Message message) { 3655 logStateAndMessage(message, getClass().getSimpleName()); 3656 switch (message.what) { 3657 case CMD_START_SUPPLICANT: 3658 if (mWifiNative.loadDriver()) { 3659 try { 3660 mNwService.wifiFirmwareReload(mInterfaceName, "STA"); 3661 } catch (Exception e) { 3662 loge("Failed to reload STA firmware " + e); 3663 // continue 3664 } 3665 3666 try { 3667 // A runtime crash can leave the interface up and 3668 // this affects connectivity when supplicant starts up. 3669 // Ensure interface is down before a supplicant start. 3670 mNwService.setInterfaceDown(mInterfaceName); 3671 // Set privacy extensions 3672 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); 3673 3674 // IPv6 is enabled only as long as access point is connected since: 3675 // - IPv6 addresses and routes stick around after disconnection 3676 // - kernel is unaware when connected and fails to start IPv6 negotiation 3677 // - kernel can start autoconfiguration when 802.1x is not complete 3678 mNwService.disableIpv6(mInterfaceName); 3679 } catch (RemoteException re) { 3680 loge("Unable to change interface settings: " + re); 3681 } catch (IllegalStateException ie) { 3682 loge("Unable to change interface settings: " + ie); 3683 } 3684 3685 /* Stop a running supplicant after a runtime restart 3686 * Avoids issues with drivers that do not handle interface down 3687 * on a running supplicant properly. 3688 */ 3689 mWifiMonitor.killSupplicant(mP2pSupported); 3690 if(mWifiNative.startSupplicant(mP2pSupported)) { 3691 setWifiState(WIFI_STATE_ENABLING); 3692 if (DBG) log("Supplicant start successful"); 3693 mWifiMonitor.startMonitoring(); 3694 transitionTo(mSupplicantStartingState); 3695 } else { 3696 loge("Failed to start supplicant!"); 3697 } 3698 } else { 3699 loge("Failed to load driver"); 3700 } 3701 break; 3702 case CMD_START_AP: 3703 if (mWifiNative.loadDriver()) { 3704 setWifiApState(WIFI_AP_STATE_ENABLING); 3705 transitionTo(mSoftApStartingState); 3706 } else { 3707 loge("Failed to load driver for softap"); 3708 } 3709 default: 3710 return NOT_HANDLED; 3711 } 3712 return HANDLED; 3713 } 3714 } 3715 3716 class SupplicantStartingState extends State { 3717 private void initializeWpsDetails() { 3718 String detail; 3719 detail = SystemProperties.get("ro.product.name", ""); 3720 if (!mWifiNative.setDeviceName(detail)) { 3721 loge("Failed to set device name " + detail); 3722 } 3723 detail = SystemProperties.get("ro.product.manufacturer", ""); 3724 if (!mWifiNative.setManufacturer(detail)) { 3725 loge("Failed to set manufacturer " + detail); 3726 } 3727 detail = SystemProperties.get("ro.product.model", ""); 3728 if (!mWifiNative.setModelName(detail)) { 3729 loge("Failed to set model name " + detail); 3730 } 3731 detail = SystemProperties.get("ro.product.model", ""); 3732 if (!mWifiNative.setModelNumber(detail)) { 3733 loge("Failed to set model number " + detail); 3734 } 3735 detail = SystemProperties.get("ro.serialno", ""); 3736 if (!mWifiNative.setSerialNumber(detail)) { 3737 loge("Failed to set serial number " + detail); 3738 } 3739 if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) { 3740 loge("Failed to set WPS config methods"); 3741 } 3742 if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) { 3743 loge("Failed to set primary device type " + mPrimaryDeviceType); 3744 } 3745 } 3746 3747 @Override 3748 public boolean processMessage(Message message) { 3749 logStateAndMessage(message, getClass().getSimpleName()); 3750 3751 switch(message.what) { 3752 case WifiMonitor.SUP_CONNECTION_EVENT: 3753 if (DBG) log("Supplicant connection established"); 3754 setWifiState(WIFI_STATE_ENABLED); 3755 mSupplicantRestartCount = 0; 3756 /* Reset the supplicant state to indicate the supplicant 3757 * state is not known at this time */ 3758 mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE); 3759 /* Initialize data structures */ 3760 mLastBssid = null; 3761 mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; 3762 mLastSignalLevel = -1; 3763 3764 mWifiInfo.setMacAddress(mWifiNative.getMacAddress()); 3765 mWifiNative.enableSaveConfig(); 3766 mWifiConfigStore.loadAndEnableAllNetworks(); 3767 initializeWpsDetails(); 3768 3769 sendSupplicantConnectionChangedBroadcast(true); 3770 transitionTo(mDriverStartedState); 3771 break; 3772 case WifiMonitor.SUP_DISCONNECTION_EVENT: 3773 if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) { 3774 loge("Failed to setup control channel, restart supplicant"); 3775 mWifiMonitor.killSupplicant(mP2pSupported); 3776 transitionTo(mInitialState); 3777 sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS); 3778 } else { 3779 loge("Failed " + mSupplicantRestartCount + 3780 " times to start supplicant, unload driver"); 3781 mSupplicantRestartCount = 0; 3782 setWifiState(WIFI_STATE_UNKNOWN); 3783 transitionTo(mInitialState); 3784 } 3785 break; 3786 case CMD_START_SUPPLICANT: 3787 case CMD_STOP_SUPPLICANT: 3788 case CMD_START_AP: 3789 case CMD_STOP_AP: 3790 case CMD_START_DRIVER: 3791 case CMD_STOP_DRIVER: 3792 case CMD_SET_OPERATIONAL_MODE: 3793 case CMD_SET_COUNTRY_CODE: 3794 case CMD_SET_FREQUENCY_BAND: 3795 case CMD_START_PACKET_FILTERING: 3796 case CMD_STOP_PACKET_FILTERING: 3797 deferMessage(message); 3798 break; 3799 default: 3800 return NOT_HANDLED; 3801 } 3802 return HANDLED; 3803 } 3804 } 3805 3806 class SupplicantStartedState extends State { 3807 @Override 3808 public void enter() { 3809 /* Wifi is available as long as we have a connection to supplicant */ 3810 mNetworkInfo.setIsAvailable(true); 3811 if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo); 3812 3813 int defaultInterval = mContext.getResources().getInteger( 3814 R.integer.config_wifi_supplicant_scan_interval); 3815 3816 mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(), 3817 Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS, 3818 defaultInterval); 3819 3820 mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000); 3821 3822 if (mFrameworkAutoJoin.get()) { 3823 mWifiNative.enableAutoConnect(false); 3824 } 3825 3826 } 3827 @Override 3828 public boolean processMessage(Message message) { 3829 logStateAndMessage(message, getClass().getSimpleName()); 3830 3831 switch(message.what) { 3832 case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */ 3833 if (mP2pSupported) { 3834 transitionTo(mWaitForP2pDisableState); 3835 } else { 3836 transitionTo(mSupplicantStoppingState); 3837 } 3838 break; 3839 case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */ 3840 loge("Connection lost, restart supplicant"); 3841 handleSupplicantConnectionLoss(); 3842 handleNetworkDisconnect(); 3843 mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE); 3844 if (mP2pSupported) { 3845 transitionTo(mWaitForP2pDisableState); 3846 } else { 3847 transitionTo(mInitialState); 3848 } 3849 sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS); 3850 break; 3851 case WifiMonitor.SCAN_RESULTS_EVENT: 3852 setScanResults(); 3853 sendScanResultsAvailableBroadcast(); 3854 mIsScanOngoing = false; 3855 mIsFullScanOngoing = false; 3856 if (mBufferedScanMsg.size() > 0) 3857 sendMessage(mBufferedScanMsg.remove()); 3858 break; 3859 case CMD_PING_SUPPLICANT: 3860 boolean ok = mWifiNative.ping(); 3861 replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); 3862 break; 3863 case CMD_GET_CAPABILITY_FREQ: 3864 String freqs = mWifiNative.getFreqCapability(); 3865 replyToMessage(message, message.what, freqs); 3866 break; 3867 case CMD_START_AP: 3868 /* Cannot start soft AP while in client mode */ 3869 loge("Failed to start soft AP with a running supplicant"); 3870 setWifiApState(WIFI_AP_STATE_FAILED); 3871 break; 3872 case CMD_SET_OPERATIONAL_MODE: 3873 mOperationalMode = message.arg1; 3874 break; 3875 default: 3876 return NOT_HANDLED; 3877 } 3878 return HANDLED; 3879 } 3880 3881 @Override 3882 public void exit() { 3883 mNetworkInfo.setIsAvailable(false); 3884 if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo); 3885 } 3886 } 3887 3888 class SupplicantStoppingState extends State { 3889 @Override 3890 public void enter() { 3891 /* Send any reset commands to supplicant before shutting it down */ 3892 handleNetworkDisconnect(); 3893 if (mDhcpStateMachine != null) { 3894 mDhcpStateMachine.doQuit(); 3895 } 3896 3897 if (DBG) log("stopping supplicant"); 3898 mWifiMonitor.stopSupplicant(); 3899 3900 /* Send ourselves a delayed message to indicate failure after a wait time */ 3901 sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED, 3902 ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS); 3903 setWifiState(WIFI_STATE_DISABLING); 3904 mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE); 3905 } 3906 @Override 3907 public boolean processMessage(Message message) { 3908 logStateAndMessage(message, getClass().getSimpleName()); 3909 3910 switch(message.what) { 3911 case WifiMonitor.SUP_CONNECTION_EVENT: 3912 loge("Supplicant connection received while stopping"); 3913 break; 3914 case WifiMonitor.SUP_DISCONNECTION_EVENT: 3915 if (DBG) log("Supplicant connection lost"); 3916 handleSupplicantConnectionLoss(); 3917 transitionTo(mInitialState); 3918 break; 3919 case CMD_STOP_SUPPLICANT_FAILED: 3920 if (message.arg1 == mSupplicantStopFailureToken) { 3921 loge("Timed out on a supplicant stop, kill and proceed"); 3922 handleSupplicantConnectionLoss(); 3923 transitionTo(mInitialState); 3924 } 3925 break; 3926 case CMD_START_SUPPLICANT: 3927 case CMD_STOP_SUPPLICANT: 3928 case CMD_START_AP: 3929 case CMD_STOP_AP: 3930 case CMD_START_DRIVER: 3931 case CMD_STOP_DRIVER: 3932 case CMD_SET_OPERATIONAL_MODE: 3933 case CMD_SET_COUNTRY_CODE: 3934 case CMD_SET_FREQUENCY_BAND: 3935 case CMD_START_PACKET_FILTERING: 3936 case CMD_STOP_PACKET_FILTERING: 3937 deferMessage(message); 3938 break; 3939 default: 3940 return NOT_HANDLED; 3941 } 3942 return HANDLED; 3943 } 3944 } 3945 3946 class DriverStartingState extends State { 3947 private int mTries; 3948 @Override 3949 public void enter() { 3950 mTries = 1; 3951 /* Send ourselves a delayed message to start driver a second time */ 3952 sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT, 3953 ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS); 3954 } 3955 @Override 3956 public boolean processMessage(Message message) { 3957 logStateAndMessage(message, getClass().getSimpleName()); 3958 3959 switch(message.what) { 3960 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 3961 SupplicantState state = handleSupplicantStateChange(message); 3962 /* If suplicant is exiting out of INTERFACE_DISABLED state into 3963 * a state that indicates driver has started, it is ready to 3964 * receive driver commands 3965 */ 3966 if (SupplicantState.isDriverActive(state)) { 3967 transitionTo(mDriverStartedState); 3968 } 3969 break; 3970 case CMD_DRIVER_START_TIMED_OUT: 3971 if (message.arg1 == mDriverStartToken) { 3972 if (mTries >= 2) { 3973 loge("Failed to start driver after " + mTries); 3974 transitionTo(mDriverStoppedState); 3975 } else { 3976 loge("Driver start failed, retrying"); 3977 mWakeLock.acquire(); 3978 mWifiNative.startDriver(); 3979 mWakeLock.release(); 3980 3981 ++mTries; 3982 /* Send ourselves a delayed message to start driver again */ 3983 sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT, 3984 ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS); 3985 } 3986 } 3987 break; 3988 /* Queue driver commands & connection events */ 3989 case CMD_START_DRIVER: 3990 case CMD_STOP_DRIVER: 3991 case WifiMonitor.NETWORK_CONNECTION_EVENT: 3992 case WifiMonitor.NETWORK_DISCONNECTION_EVENT: 3993 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: 3994 case WifiMonitor.ASSOCIATION_REJECTION_EVENT: 3995 case WifiMonitor.WPS_OVERLAP_EVENT: 3996 case CMD_SET_COUNTRY_CODE: 3997 case CMD_SET_FREQUENCY_BAND: 3998 case CMD_START_PACKET_FILTERING: 3999 case CMD_STOP_PACKET_FILTERING: 4000 case CMD_START_SCAN: 4001 case CMD_DISCONNECT: 4002 case CMD_REASSOCIATE: 4003 case CMD_RECONNECT: 4004 deferMessage(message); 4005 break; 4006 default: 4007 return NOT_HANDLED; 4008 } 4009 return HANDLED; 4010 } 4011 } 4012 4013 class DriverStartedState extends State { 4014 @Override 4015 public void enter() { 4016 4017 if (PDBG) { 4018 loge("Driverstarted State enter"); 4019 } 4020 mIsRunning = true; 4021 mInDelayedStop = false; 4022 mDelayedStopCounter++; 4023 updateBatteryWorkSource(null); 4024 /** 4025 * Enable bluetooth coexistence scan mode when bluetooth connection is active. 4026 * When this mode is on, some of the low-level scan parameters used by the 4027 * driver are changed to reduce interference with bluetooth 4028 */ 4029 mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive); 4030 /* set country code */ 4031 setCountryCode(); 4032 /* set frequency band of operation */ 4033 setFrequencyBand(); 4034 /* initialize network state */ 4035 setNetworkDetailedState(DetailedState.DISCONNECTED); 4036 4037 /* Remove any filtering on Multicast v6 at start */ 4038 mWifiNative.stopFilteringMulticastV6Packets(); 4039 4040 /* Reset Multicast v4 filtering state */ 4041 if (mFilteringMulticastV4Packets.get()) { 4042 mWifiNative.startFilteringMulticastV4Packets(); 4043 } else { 4044 mWifiNative.stopFilteringMulticastV4Packets(); 4045 } 4046 4047 mDhcpActive = false; 4048 4049 startBatchedScan(); 4050 4051 if (mOperationalMode != CONNECT_MODE) { 4052 mWifiNative.disconnect(); 4053 mWifiConfigStore.disableAllNetworks(); 4054 if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) { 4055 setWifiState(WIFI_STATE_DISABLED); 4056 } 4057 transitionTo(mScanModeState); 4058 } else { 4059 /* Driver stop may have disabled networks, enable right after start */ 4060 mWifiConfigStore.enableAllNetworks(); 4061 4062 if (DBG) loge("Attempting to reconnect to wifi network .."); 4063 mWifiNative.reconnect(); 4064 4065 // Status pulls in the current supplicant state and network connection state 4066 // events over the monitor connection. This helps framework sync up with 4067 // current supplicant state 4068 mWifiNative.status(); 4069 transitionTo(mDisconnectedState); 4070 } 4071 4072 // We may have missed screen update at boot 4073 if (mScreenBroadcastReceived.get() == false) { 4074 PowerManager powerManager = (PowerManager)mContext.getSystemService( 4075 Context.POWER_SERVICE); 4076 handleScreenStateChanged(powerManager.isScreenOn()); 4077 } else { 4078 // Set the right suspend mode settings 4079 mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0 4080 && mUserWantsSuspendOpt.get()); 4081 } 4082 mWifiNative.setPowerSave(true); 4083 4084 if (mP2pSupported) { 4085 if (mOperationalMode == CONNECT_MODE) { 4086 mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P); 4087 } else { 4088 // P2P statemachine starts in disabled state, and is not enabled until 4089 // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to 4090 // keep it disabled. 4091 } 4092 } 4093 4094 final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE); 4095 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 4096 intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED); 4097 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 4098 4099 if (PDBG) { 4100 loge("Driverstarted State enter done"); 4101 } 4102 } 4103 4104 @Override 4105 public boolean processMessage(Message message) { 4106 logStateAndMessage(message, getClass().getSimpleName()); 4107 4108 switch(message.what) { 4109 case CMD_START_SCAN: 4110 handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message); 4111 break; 4112 case CMD_SET_BATCHED_SCAN: 4113 if (recordBatchedScanSettings(message.arg1, message.arg2, 4114 (Bundle)message.obj)) { 4115 if (mBatchedScanSettings != null) { 4116 startBatchedScan(); 4117 } else { 4118 stopBatchedScan(); 4119 } 4120 } 4121 break; 4122 case CMD_SET_COUNTRY_CODE: 4123 String country = (String) message.obj; 4124 final boolean persist = (message.arg2 == 1); 4125 final int sequence = message.arg1; 4126 if (sequence != mCountryCodeSequence.get()) { 4127 if (DBG) log("set country code ignored due to sequnce num"); 4128 break; 4129 } 4130 if (DBG) log("set country code " + country); 4131 if (persist) { 4132 mPersistedCountryCode = country; 4133 Settings.Global.putString(mContext.getContentResolver(), 4134 Settings.Global.WIFI_COUNTRY_CODE, 4135 country); 4136 } 4137 country = country.toUpperCase(Locale.ROOT); 4138 if (mLastSetCountryCode == null 4139 || country.equals(mLastSetCountryCode) == false) { 4140 if (mWifiNative.setCountryCode(country)) { 4141 mLastSetCountryCode = country; 4142 } else { 4143 loge("Failed to set country code " + country); 4144 } 4145 } 4146 mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, country); 4147 break; 4148 case CMD_SET_FREQUENCY_BAND: 4149 int band = message.arg1; 4150 if (DBG) log("set frequency band " + band); 4151 if (mWifiNative.setBand(band)) { 4152 4153 if (PDBG) loge("did set frequency band " + band); 4154 4155 mFrequencyBand.set(band); 4156 // flush old data - like scan results 4157 mWifiNative.bssFlush(); 4158 // fetch the latest scan results when frequency band is set 4159 if (mFrameworkAutoJoin.get()) 4160 startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null); 4161 else 4162 startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP, null); 4163 if (PDBG) loge("done set frequency band " + band); 4164 4165 } else { 4166 loge("Failed to set frequency band " + band); 4167 } 4168 break; 4169 case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE: 4170 mBluetoothConnectionActive = (message.arg1 != 4171 BluetoothAdapter.STATE_DISCONNECTED); 4172 mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive); 4173 break; 4174 case CMD_STOP_DRIVER: 4175 int mode = message.arg1; 4176 4177 /* Already doing a delayed stop */ 4178 if (mInDelayedStop) { 4179 if (DBG) log("Already in delayed stop"); 4180 break; 4181 } 4182 /* disconnect right now, but leave the driver running for a bit */ 4183 mWifiConfigStore.disableAllNetworks(); 4184 4185 mInDelayedStop = true; 4186 mDelayedStopCounter++; 4187 if (DBG) log("Delayed stop message " + mDelayedStopCounter); 4188 4189 /* send regular delayed shut down */ 4190 Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null); 4191 driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter); 4192 mDriverStopIntent = PendingIntent.getBroadcast(mContext, 4193 DRIVER_STOP_REQUEST, driverStopIntent, 4194 PendingIntent.FLAG_UPDATE_CURRENT); 4195 4196 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() 4197 + mDriverStopDelayMs, mDriverStopIntent); 4198 break; 4199 case CMD_START_DRIVER: 4200 if (mInDelayedStop) { 4201 mInDelayedStop = false; 4202 mDelayedStopCounter++; 4203 mAlarmManager.cancel(mDriverStopIntent); 4204 if (DBG) log("Delayed stop ignored due to start"); 4205 if (mOperationalMode == CONNECT_MODE) { 4206 mWifiConfigStore.enableAllNetworks(); 4207 } 4208 } 4209 break; 4210 case CMD_DELAYED_STOP_DRIVER: 4211 if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter); 4212 if (message.arg1 != mDelayedStopCounter) break; 4213 if (getCurrentState() != mDisconnectedState) { 4214 mWifiNative.disconnect(); 4215 handleNetworkDisconnect(); 4216 } 4217 mWakeLock.acquire(); 4218 mWifiNative.stopDriver(); 4219 mWakeLock.release(); 4220 if (mP2pSupported) { 4221 transitionTo(mWaitForP2pDisableState); 4222 } else { 4223 transitionTo(mDriverStoppingState); 4224 } 4225 break; 4226 case CMD_START_PACKET_FILTERING: 4227 if (message.arg1 == MULTICAST_V6) { 4228 mWifiNative.startFilteringMulticastV6Packets(); 4229 } else if (message.arg1 == MULTICAST_V4) { 4230 mWifiNative.startFilteringMulticastV4Packets(); 4231 } else { 4232 loge("Illegal arugments to CMD_START_PACKET_FILTERING"); 4233 } 4234 break; 4235 case CMD_STOP_PACKET_FILTERING: 4236 if (message.arg1 == MULTICAST_V6) { 4237 mWifiNative.stopFilteringMulticastV6Packets(); 4238 } else if (message.arg1 == MULTICAST_V4) { 4239 mWifiNative.stopFilteringMulticastV4Packets(); 4240 } else { 4241 loge("Illegal arugments to CMD_STOP_PACKET_FILTERING"); 4242 } 4243 break; 4244 case CMD_SET_SUSPEND_OPT_ENABLED: 4245 if (message.arg1 == 1) { 4246 setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true); 4247 mSuspendWakeLock.release(); 4248 } else { 4249 setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false); 4250 } 4251 break; 4252 case CMD_SET_HIGH_PERF_MODE: 4253 if (message.arg1 == 1) { 4254 setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false); 4255 } else { 4256 setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true); 4257 } 4258 break; 4259 case CMD_ENABLE_TDLS: 4260 if (message.obj != null) { 4261 String remoteAddress = (String) message.obj; 4262 boolean enable = (message.arg1 == 1); 4263 mWifiNative.startTdls(remoteAddress, enable); 4264 } 4265 break; 4266 default: 4267 return NOT_HANDLED; 4268 } 4269 return HANDLED; 4270 } 4271 @Override 4272 public void exit() { 4273 mIsRunning = false; 4274 updateBatteryWorkSource(null); 4275 mScanResults = new ArrayList<ScanResult>(); 4276 4277 stopBatchedScan(); 4278 4279 final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE); 4280 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 4281 intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED); 4282 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 4283 noteScanEnd(); // wrap up any pending request. 4284 mBufferedScanMsg.clear(); 4285 4286 mLastSetCountryCode = null; 4287 } 4288 } 4289 4290 class WaitForP2pDisableState extends State { 4291 private State mTransitionToState; 4292 @Override 4293 public void enter() { 4294 switch (getCurrentMessage().what) { 4295 case WifiMonitor.SUP_DISCONNECTION_EVENT: 4296 mTransitionToState = mInitialState; 4297 break; 4298 case CMD_DELAYED_STOP_DRIVER: 4299 mTransitionToState = mDriverStoppingState; 4300 break; 4301 case CMD_STOP_SUPPLICANT: 4302 mTransitionToState = mSupplicantStoppingState; 4303 break; 4304 default: 4305 mTransitionToState = mDriverStoppingState; 4306 break; 4307 } 4308 mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ); 4309 } 4310 @Override 4311 public boolean processMessage(Message message) { 4312 logStateAndMessage(message, getClass().getSimpleName()); 4313 4314 switch(message.what) { 4315 case WifiStateMachine.CMD_DISABLE_P2P_RSP: 4316 transitionTo(mTransitionToState); 4317 break; 4318 /* Defer wifi start/shut and driver commands */ 4319 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 4320 case CMD_START_SUPPLICANT: 4321 case CMD_STOP_SUPPLICANT: 4322 case CMD_START_AP: 4323 case CMD_STOP_AP: 4324 case CMD_START_DRIVER: 4325 case CMD_STOP_DRIVER: 4326 case CMD_SET_OPERATIONAL_MODE: 4327 case CMD_SET_COUNTRY_CODE: 4328 case CMD_SET_FREQUENCY_BAND: 4329 case CMD_START_PACKET_FILTERING: 4330 case CMD_STOP_PACKET_FILTERING: 4331 case CMD_START_SCAN: 4332 case CMD_DISCONNECT: 4333 case CMD_REASSOCIATE: 4334 case CMD_RECONNECT: 4335 deferMessage(message); 4336 break; 4337 default: 4338 return NOT_HANDLED; 4339 } 4340 return HANDLED; 4341 } 4342 } 4343 4344 class DriverStoppingState extends State { 4345 @Override 4346 public boolean processMessage(Message message) { 4347 logStateAndMessage(message, getClass().getSimpleName()); 4348 4349 switch(message.what) { 4350 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 4351 SupplicantState state = handleSupplicantStateChange(message); 4352 if (state == SupplicantState.INTERFACE_DISABLED) { 4353 transitionTo(mDriverStoppedState); 4354 } 4355 break; 4356 /* Queue driver commands */ 4357 case CMD_START_DRIVER: 4358 case CMD_STOP_DRIVER: 4359 case CMD_SET_COUNTRY_CODE: 4360 case CMD_SET_FREQUENCY_BAND: 4361 case CMD_START_PACKET_FILTERING: 4362 case CMD_STOP_PACKET_FILTERING: 4363 case CMD_START_SCAN: 4364 case CMD_DISCONNECT: 4365 case CMD_REASSOCIATE: 4366 case CMD_RECONNECT: 4367 deferMessage(message); 4368 break; 4369 default: 4370 return NOT_HANDLED; 4371 } 4372 return HANDLED; 4373 } 4374 } 4375 4376 class DriverStoppedState extends State { 4377 @Override 4378 public boolean processMessage(Message message) { 4379 logStateAndMessage(message, getClass().getSimpleName()); 4380 switch (message.what) { 4381 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 4382 StateChangeResult stateChangeResult = (StateChangeResult) message.obj; 4383 SupplicantState state = stateChangeResult.state; 4384 // A WEXT bug means that we can be back to driver started state 4385 // unexpectedly 4386 if (SupplicantState.isDriverActive(state)) { 4387 transitionTo(mDriverStartedState); 4388 } 4389 break; 4390 case CMD_START_DRIVER: 4391 mWakeLock.acquire(); 4392 mWifiNative.startDriver(); 4393 mWakeLock.release(); 4394 transitionTo(mDriverStartingState); 4395 break; 4396 default: 4397 return NOT_HANDLED; 4398 } 4399 return HANDLED; 4400 } 4401 } 4402 4403 class ScanModeState extends State { 4404 private int mLastOperationMode; 4405 @Override 4406 public void enter() { 4407 mLastOperationMode = mOperationalMode; 4408 } 4409 @Override 4410 public boolean processMessage(Message message) { 4411 logStateAndMessage(message, getClass().getSimpleName()); 4412 4413 switch(message.what) { 4414 case CMD_SET_OPERATIONAL_MODE: 4415 if (message.arg1 == CONNECT_MODE) { 4416 4417 if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) { 4418 setWifiState(WIFI_STATE_ENABLED); 4419 // Load and re-enable networks when going back to enabled state 4420 // This is essential for networks to show up after restore 4421 mWifiConfigStore.loadAndEnableAllNetworks(); 4422 mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P); 4423 } else { 4424 mWifiConfigStore.enableAllNetworks(); 4425 } 4426 4427 mWifiNative.reconnect(); 4428 4429 mOperationalMode = CONNECT_MODE; 4430 transitionTo(mDisconnectedState); 4431 } else { 4432 // Nothing to do 4433 return HANDLED; 4434 } 4435 break; 4436 // Handle scan. All the connection related commands are 4437 // handled only in ConnectModeState 4438 case CMD_START_SCAN: 4439 handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message); 4440 break; 4441 default: 4442 return NOT_HANDLED; 4443 } 4444 return HANDLED; 4445 } 4446 } 4447 4448 4449 String smToString(Message message) { 4450 String s = "unknown"; 4451 switch(message.what) { 4452 case WifiMonitor.DRIVER_HUNG_EVENT: 4453 s = "DRIVER_HUNG_EVENT"; 4454 break; 4455 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 4456 s = "AsyncChannel.CMD_CHANNEL_HALF_CONNECTED"; 4457 break; 4458 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 4459 s = "AsyncChannel.CMD_CHANNEL_DISCONNECTED"; 4460 break; 4461 case CMD_SET_FREQUENCY_BAND: 4462 s = "CMD_SET_FREQUENCY_BAND"; 4463 break; 4464 case CMD_START_DRIVER: 4465 s = "CMD_START_DRIVER"; 4466 break; 4467 case CMD_STOP_DRIVER: 4468 s = "CMD_STOP_DRIVER"; 4469 break; 4470 case CMD_STOP_SUPPLICANT: 4471 s = "CMD_STOP_SUPPLICANT"; 4472 break; 4473 case CMD_START_SUPPLICANT: 4474 s = "CMD_START_SUPPLICANT"; 4475 break; 4476 case CMD_REQUEST_AP_CONFIG: 4477 s = "CMD_REQUEST_AP_CONFIG"; 4478 break; 4479 case CMD_RESPONSE_AP_CONFIG: 4480 s = "CMD_RESPONSE_AP_CONFIG"; 4481 break; 4482 case CMD_TETHER_STATE_CHANGE: 4483 s = "CMD_TETHER_STATE_CHANGE"; 4484 break; 4485 case CMD_TETHER_NOTIFICATION_TIMED_OUT: 4486 s = "CMD_TETHER_NOTIFICATION_TIMED_OUT"; 4487 break; 4488 case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE: 4489 s = "CMD_BLUETOOTH_ADAPTER_STATE_CHANGE"; 4490 break; 4491 case CMD_ADD_OR_UPDATE_NETWORK: 4492 s= "CMD_ADD_OR_UPDATE_NETWORK"; 4493 break; 4494 case CMD_REMOVE_NETWORK: 4495 s= "CMD_REMOVE_NETWORK"; 4496 break; 4497 case CMD_ENABLE_NETWORK: 4498 s= "CMD_ENABLE_NETWORK"; 4499 break; 4500 case CMD_ENABLE_ALL_NETWORKS: 4501 s= "CMD_ENABLE_ALL_NETWORKS"; 4502 break; 4503 case CMD_AUTO_CONNECT: 4504 s = "CMD_AUTO_CONNECT"; 4505 break; 4506 case CMD_AUTO_ROAM: 4507 s = "CMD_AUTO_ROAM"; 4508 break; 4509 case CMD_BOOT_COMPLETED: 4510 s = "CMD_BOOT_COMPLETED"; 4511 break; 4512 case DhcpStateMachine.CMD_START_DHCP: 4513 s = "CMD_START_DHCP"; 4514 break; 4515 case DhcpStateMachine.CMD_STOP_DHCP: 4516 s = "CMD_STOP_DHCP"; 4517 break; 4518 case DhcpStateMachine.CMD_RENEW_DHCP: 4519 s = "CMD_RENEW_DHCP"; 4520 break; 4521 case DhcpStateMachine.CMD_PRE_DHCP_ACTION: 4522 s = "CMD_PRE_DHCP_ACTION"; 4523 break; 4524 case DhcpStateMachine.CMD_POST_DHCP_ACTION: 4525 s = "CMD_POST_DHCP_ACTION"; 4526 break; 4527 case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE: 4528 s = "CMD_PRE_DHCP_ACTION_COMPLETE"; 4529 break; 4530 case DhcpStateMachine.CMD_ON_QUIT: 4531 s = "CMD_ON_QUIT"; 4532 break; 4533 case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST: 4534 s = "WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST"; 4535 break; 4536 case WifiManager.DISABLE_NETWORK: 4537 s="WifiManager.DISABLE_NETWORK"; 4538 break; 4539 case CMD_BLACKLIST_NETWORK: 4540 s="CMD_BLACKLIST_NETWORK"; 4541 break; 4542 case CMD_CLEAR_BLACKLIST: 4543 s="CMD_CLEAR_BLACKLIST"; 4544 break; 4545 case CMD_SAVE_CONFIG: 4546 s="CMD_SAVE_CONFIG"; 4547 break; 4548 case CMD_GET_CONFIGURED_NETWORKS: 4549 s="CMD_GET_CONFIGURED_NETWORKS"; 4550 break; 4551 case CMD_DISCONNECT: 4552 s="CMD_DISCONNECT"; 4553 break; 4554 case CMD_RECONNECT: 4555 s= "CMD_RECONNECT"; 4556 break; 4557 case CMD_REASSOCIATE: 4558 s= "CMD_REASSOCIATE"; 4559 break; 4560 case CMD_SET_HIGH_PERF_MODE: 4561 s="CMD_SET_HIGH_PERF_MODE"; 4562 break; 4563 case CMD_SET_COUNTRY_CODE: 4564 s="CMD_SET_COUNTRY_CODE"; 4565 break; 4566 case CMD_ENABLE_RSSI_POLL: 4567 s="CMD_ENABLE_RSSI_POLL"; 4568 break; 4569 case CMD_RSSI_POLL: 4570 s="CMD_RSSI_POLL"; 4571 break; 4572 case CMD_START_PACKET_FILTERING: 4573 s="CMD_START_PACKET_FILTERING"; 4574 break; 4575 case CMD_STOP_PACKET_FILTERING: 4576 s="CMD_STOP_PACKET_FILTERING"; 4577 break; 4578 case CMD_SET_SUSPEND_OPT_ENABLED: 4579 s="CMD_SET_SUSPEND_OPT_ENABLED"; 4580 break; 4581 case CMD_NO_NETWORKS_PERIODIC_SCAN: 4582 s="CMD_NO_NETWORKS_PERIODIC_SCAN"; 4583 break; 4584 case CMD_SET_BATCHED_SCAN: 4585 s="CMD_SET_BATCHED_SCAN"; 4586 break; 4587 case CMD_START_NEXT_BATCHED_SCAN: 4588 s="CMD_START_NEXT_BATCHED_SCAN"; 4589 break; 4590 case CMD_POLL_BATCHED_SCAN: 4591 s="CMD_POLL_BATCHED_SCAN"; 4592 break; 4593 case CMD_NETLINK_UPDATE: 4594 s="CMD_NETLINK_UPDATE"; 4595 break; 4596 case CMD_RELOAD_TLS_AND_RECONNECT: 4597 s= "CMD_RELOAD_TLS_AND_RECONNECT"; 4598 break; 4599 case WifiManager.CONNECT_NETWORK: 4600 s= "CONNECT_NETWORK"; 4601 break; 4602 case WifiManager.SAVE_NETWORK: 4603 s= "SAVE_NETWORK"; 4604 break; 4605 case WifiManager.FORGET_NETWORK: 4606 s = "FORGET_NETWORK"; 4607 break; 4608 case WifiManager.START_WPS: 4609 s= "START_WPS"; 4610 break; 4611 case WifiMonitor.SUP_CONNECTION_EVENT: 4612 s= "SUP_CONNECTION_EVENT"; 4613 break; 4614 case WifiMonitor.SUP_DISCONNECTION_EVENT: 4615 s= "SUP_DISCONNECTION_EVENT"; 4616 break; 4617 case WifiMonitor.SCAN_RESULTS_EVENT: 4618 s= "SCAN_RESULTS_EVENT"; 4619 break; 4620 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 4621 s= "SUPPLICANT_STATE_CHANGE_EVENT"; 4622 break; 4623 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: 4624 s= "AUTHENTICATION_FAILURE_EVENT"; 4625 break; 4626 case WifiMonitor.SSID_TEMP_DISABLED: 4627 s= "SSID_TEMP_DISABLED"; 4628 break; 4629 case WifiMonitor.SSID_REENABLED: 4630 s= "SSID_REENABLED"; 4631 break; 4632 case WifiMonitor.WPS_SUCCESS_EVENT: 4633 s= "WPS_SUCCESS_EVENT"; 4634 break; 4635 case WifiMonitor.WPS_FAIL_EVENT: 4636 s= "WPS_FAIL_EVENT"; 4637 break; 4638 case WifiMonitor.SUP_REQUEST_IDENTITY: 4639 s = "SUP_REQUEST_IDENTITY"; 4640 break; 4641 case WifiMonitor.NETWORK_CONNECTION_EVENT: 4642 s= "NETWORK_CONNECTION_EVENT"; 4643 break; 4644 case WifiMonitor.NETWORK_DISCONNECTION_EVENT: 4645 s="NETWORK_DISCONNECTION_EVENT"; 4646 break; 4647 case WifiMonitor.ASSOCIATION_REJECTION_EVENT: 4648 s ="ASSOCIATION_REJECTION_EVENT"; 4649 break; 4650 case CMD_SET_OPERATIONAL_MODE: 4651 s="CMD_SET_OPERATIONAL_MODE"; 4652 break; 4653 case CMD_START_SCAN: 4654 s="CMD_START_SCAN"; 4655 break; 4656 case CMD_ENABLE_BACKGROUND_SCAN: 4657 s="CMD_ENABLE_BACKGROUND_SCAN"; 4658 break; 4659 case CMD_DISABLE_P2P_RSP: 4660 s="CMD_DISABLE_P2P_RSP"; 4661 break; 4662 case CMD_DISABLE_P2P_REQ: 4663 s="CMD_DISABLE_P2P_REQ"; 4664 break; 4665 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED: 4666 s = "GOOD_LINK_DETECTED"; 4667 break; 4668 case WifiWatchdogStateMachine.POOR_LINK_DETECTED: 4669 s = "POOR_LINK_DETECTED"; 4670 break; 4671 default: 4672 s = "what:" + Integer.toString(message.what); 4673 break; 4674 } 4675 return s; 4676 } 4677 4678 void registerConnected() { 4679 if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) { 4680 long now_ms = System.currentTimeMillis(); 4681 //we are switching away from this configuration, 4682 //hence record the time we were connected last 4683 WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId); 4684 if (config != null) { 4685 config.lastConnected = System.currentTimeMillis(); 4686 } 4687 } 4688 } 4689 4690 void registerDisconnected() { 4691 if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) { 4692 long now_ms = System.currentTimeMillis(); 4693 //we are switching away from this configuration, 4694 //hence record the time we were connected last 4695 WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId); 4696 if (config != null) { 4697 config.lastDisconnected = System.currentTimeMillis(); 4698 } 4699 } 4700 } 4701 4702 WifiConfiguration getCurrentWifiConfiguration() { 4703 if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) { 4704 return null; 4705 } 4706 return mWifiConfigStore.getWifiConfiguration(mLastNetworkId); 4707 } 4708 4709 String getCurrentBSSID() { 4710 return mLastBssid; 4711 } 4712 4713 class ConnectModeState extends State { 4714 @Override 4715 public boolean processMessage(Message message) { 4716 WifiConfiguration config; 4717 int netId; 4718 boolean ok; 4719 String bssid; 4720 String ssid; 4721 NetworkUpdateResult result; 4722 logStateAndMessage(message, getClass().getSimpleName()); 4723 4724 switch(message.what) { 4725 case WifiMonitor.ASSOCIATION_REJECTION_EVENT: 4726 bssid = (String)message.obj; 4727 if (bssid == null || bssid.length() == 0) 4728 bssid = mTargetRoamBSSID; 4729 if (bssid != null) { 4730 mWifiConfigStore.handleBSSIDBlackList(mLastNetworkId, bssid, false); 4731 } 4732 mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT); 4733 break; 4734 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: 4735 mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT); 4736 break; 4737 case WifiMonitor.SSID_TEMP_DISABLED: 4738 case WifiMonitor.SSID_REENABLED: 4739 String substr = (String)message.obj; 4740 String en = message.what == WifiMonitor.SSID_TEMP_DISABLED ? 4741 "temp-disabled" : "re-enabled"; 4742 loge("ConnectModeState SSID state=" + en + " nid=" 4743 + Integer.toString(message.arg1) + " [" + substr + "]"); 4744 mWifiConfigStore.handleSSIDStateChange(message.arg1, message.what == 4745 WifiMonitor.SSID_REENABLED, substr); 4746 break; 4747 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 4748 SupplicantState state = handleSupplicantStateChange(message); 4749 // A driver/firmware hang can now put the interface in a down state. 4750 // We detect the interface going down and recover from it 4751 if (!SupplicantState.isDriverActive(state)) { 4752 if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) { 4753 handleNetworkDisconnect(); 4754 } 4755 log("Detected an interface down, restart driver"); 4756 transitionTo(mDriverStoppedState); 4757 sendMessage(CMD_START_DRIVER); 4758 break; 4759 } 4760 4761 // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT 4762 // when authentication times out after a successful connection, 4763 // we can figure this from the supplicant state. If supplicant 4764 // state is DISCONNECTED, but the mNetworkInfo says we are not 4765 // disconnected, we need to handle a disconnection 4766 if (state == SupplicantState.DISCONNECTED && 4767 mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) { 4768 if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect"); 4769 handleNetworkDisconnect(); 4770 transitionTo(mDisconnectedState); 4771 } 4772 break; 4773 case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST: 4774 if (message.arg1 == 1) { 4775 mWifiNative.disconnect(); 4776 mTemporarilyDisconnectWifi = true; 4777 } else { 4778 mWifiNative.reconnect(); 4779 mTemporarilyDisconnectWifi = false; 4780 } 4781 break; 4782 case CMD_ADD_OR_UPDATE_NETWORK: 4783 config = (WifiConfiguration) message.obj; 4784 replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK, 4785 mWifiConfigStore.addOrUpdateNetwork(config)); 4786 break; 4787 case CMD_REMOVE_NETWORK: 4788 ok = mWifiConfigStore.removeNetwork(message.arg1); 4789 replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); 4790 break; 4791 case CMD_ENABLE_NETWORK: 4792 boolean others = message.arg2 == 1; 4793 // Tell autojoin the user did try to select to that network 4794 // However, do NOT persist the choice by bumping the priority of the network 4795 if (others) { 4796 mWifiAutoJoinController. 4797 updateConfigurationHistory(message.arg1, true, false); 4798 // Set the last selected configuration so as to allow the system to 4799 // stick the last user choice without persisting the choice 4800 mWifiConfigStore.setLastSelectedConfiguration(message.arg1); 4801 } 4802 //cancel auto roam requests 4803 autoRoamSetBSSID(message.arg1, "any"); 4804 4805 ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1); 4806 replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); 4807 break; 4808 case CMD_ENABLE_ALL_NETWORKS: 4809 long time = android.os.SystemClock.elapsedRealtime(); 4810 if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) { 4811 mWifiConfigStore.enableAllNetworks(); 4812 mLastEnableAllNetworksTime = time; 4813 } 4814 break; 4815 case WifiManager.DISABLE_NETWORK: 4816 if (mWifiConfigStore.disableNetwork(message.arg1, 4817 WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) { 4818 replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED); 4819 } else { 4820 replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED, 4821 WifiManager.ERROR); 4822 } 4823 break; 4824 case CMD_BLACKLIST_NETWORK: 4825 mWifiNative.addToBlacklist((String)message.obj); 4826 break; 4827 case CMD_CLEAR_BLACKLIST: 4828 mWifiNative.clearBlacklist(); 4829 break; 4830 case CMD_SAVE_CONFIG: 4831 ok = mWifiConfigStore.saveConfig(); 4832 4833 if (DBG) loge("wifistatemachine did save config " + ok); 4834 replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE); 4835 4836 // Inform the backup manager about a data change 4837 IBackupManager ibm = IBackupManager.Stub.asInterface( 4838 ServiceManager.getService(Context.BACKUP_SERVICE)); 4839 if (ibm != null) { 4840 try { 4841 ibm.dataChanged("com.android.providers.settings"); 4842 } catch (Exception e) { 4843 // Try again later 4844 } 4845 } 4846 break; 4847 case CMD_GET_CONFIGURED_NETWORKS: 4848 replyToMessage(message, message.what, 4849 mWifiConfigStore.getConfiguredNetworks()); 4850 break; 4851 case WifiMonitor.SUP_REQUEST_IDENTITY: 4852 //supplicant lacks credentials to connect to that network, hence black list 4853 ssid = (String)message.obj; 4854 4855 if (targetWificonfiguration != null && ssid != null 4856 && targetWificonfiguration.SSID != null 4857 && targetWificonfiguration.SSID.equals("\"" + ssid + "\"")) { 4858 mWifiConfigStore.handleSSIDStateChange(targetWificonfiguration.networkId, 4859 false, "AUTH_FAILED no identity"); 4860 } 4861 //disconnect now, as we don't have any way to fullfill the supplicant request 4862 //fall thru 4863 case CMD_DISCONNECT: 4864 mWifiConfigStore.setLastSelectedConfiguration 4865 (WifiConfiguration.INVALID_NETWORK_ID); 4866 mWifiNative.disconnect(); 4867 break; 4868 case CMD_RECONNECT: 4869 lastConnectAttempt = System.currentTimeMillis(); 4870 mWifiNative.reconnect(); 4871 break; 4872 case CMD_REASSOCIATE: 4873 lastConnectAttempt = System.currentTimeMillis(); 4874 mWifiNative.reassociate(); 4875 break; 4876 case CMD_RELOAD_TLS_AND_RECONNECT: 4877 if (mWifiConfigStore.needsUnlockedKeyStore()) { 4878 logd("Reconnecting to give a chance to un-connected TLS networks"); 4879 mWifiNative.disconnect(); 4880 lastConnectAttempt = System.currentTimeMillis(); 4881 mWifiNative.reconnect(); 4882 } 4883 break; 4884 case CMD_AUTO_ROAM: 4885 return HANDLED; 4886 case CMD_AUTO_CONNECT: 4887 /* Work Around: wpa_supplicant can get in a bad state where it returns a non 4888 * associated status thus the STATUS command but somehow-someplace still thinks 4889 * it is associated and thus will ignore select/reconnect command with 4890 * following message: 4891 * "Already associated with the selected network - do nothing" 4892 * 4893 * Hence, sends a disconnect to supplicant first. 4894 */ 4895 mWifiNative.disconnect(); 4896 4897 /* connect command coming from auto-join */ 4898 config = (WifiConfiguration) message.obj; 4899 netId = message.arg1; 4900 int roam = message.arg2; 4901 loge("CMD_AUTO_CONNECT sup state " 4902 + mSupplicantStateTracker.getSupplicantStateName() 4903 + " my state " + getCurrentState().getName() 4904 + " nid=" + Integer.toString(netId) 4905 + " roam=" + Integer.toString(roam)); 4906 if (config == null) { 4907 loge("AUTO_CONNECT and no config, bail out..."); 4908 break; 4909 } 4910 4911 /* make sure we cancel any previous roam request */ 4912 autoRoamSetBSSID(config, "any"); 4913 4914 /* Save the network config */ 4915 loge("CMD_AUTO_CONNECT will save config -> " + config.SSID 4916 + " nid=" + Integer.toString(netId)); 4917 result = mWifiConfigStore.saveNetwork(config); 4918 netId = result.getNetworkId(); 4919 loge("CMD_AUTO_CONNECT did save config -> " 4920 + " nid=" + Integer.toString(netId)); 4921 4922 if (mWifiConfigStore.selectNetwork(netId) && 4923 mWifiNative.reconnect()) { 4924 lastConnectAttempt = System.currentTimeMillis(); 4925 targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId); 4926 // we selected a better config, 4927 // maybe because we could not see the last user 4928 // selection, then forget it. We will remember the selection 4929 // only if it was persisted. 4930 mWifiConfigStore. 4931 setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID); 4932 4933 /* The state tracker handles enabling networks upon completion/failure */ 4934 mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK); 4935 //replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED); 4936 mAutoRoaming = roam; 4937 if (isRoaming()) { 4938 transitionTo(mRoamingState); 4939 } else { 4940 transitionTo(mDisconnectingState); 4941 } 4942 } else { 4943 loge("Failed to connect config: " + config + " netId: " + netId); 4944 replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, 4945 WifiManager.ERROR); 4946 break; 4947 } 4948 break; 4949 case WifiManager.CONNECT_NETWORK: 4950 /* The connect message can contain a network id passed as arg1 on message or 4951 * or a config passed as obj on message. 4952 * For a new network, a config is passed to create and connect. 4953 * For an existing network, a network id is passed 4954 */ 4955 netId = message.arg1; 4956 config = (WifiConfiguration) message.obj; 4957 4958 if (config == null) { 4959 loge("CONNECT_NETWORK id=" + Integer.toString(netId) + " " 4960 + mSupplicantStateTracker.getSupplicantStateName() + " my state " 4961 + getCurrentState().getName()); 4962 } else { 4963 loge("CONNECT_NETWORK id=" + Integer.toString(netId) 4964 + " config=" + config.SSID 4965 + " cnid=" + config.networkId 4966 + " supstate=" + mSupplicantStateTracker.getSupplicantStateName() 4967 + " my state " + getCurrentState().getName()); 4968 } 4969 4970 autoRoamSetBSSID(netId, "any"); 4971 mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE; 4972 4973 /* Save the network config */ 4974 if (config != null) { 4975 result = mWifiConfigStore.saveNetwork(config); 4976 netId = result.getNetworkId(); 4977 } 4978 if (mFrameworkAutoJoin.get()) { 4979 /* Tell autojoin the user did try to connect to that network */ 4980 mWifiAutoJoinController.updateConfigurationHistory(netId, true, true); 4981 } 4982 mWifiConfigStore.setLastSelectedConfiguration(netId); 4983 4984 if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID 4985 && mLastNetworkId != netId) { 4986 /* Supplicant will ignore the reconnect if we are currently associated, 4987 * hence trigger a disconnect 4988 **/ 4989 mWifiNative.disconnect(); 4990 } 4991 4992 if (mWifiConfigStore.selectNetwork(netId) && 4993 mWifiNative.reconnect()) { 4994 lastConnectAttempt = System.currentTimeMillis(); 4995 targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId); 4996 4997 /* The state tracker handles enabling networks upon completion/failure */ 4998 mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK); 4999 replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED); 5000 /* Expect a disconnection from the old connection */ 5001 transitionTo(mDisconnectingState); 5002 } else { 5003 loge("Failed to connect config: " + config + " netId: " + netId); 5004 replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, 5005 WifiManager.ERROR); 5006 break; 5007 } 5008 break; 5009 case WifiManager.SAVE_NETWORK: 5010 config = (WifiConfiguration) message.obj; 5011 int nid = config.networkId; 5012 if (config == null) { 5013 loge("SAVE_NETWORK id=" + Integer.toString(nid) 5014 + " " + mSupplicantStateTracker.getSupplicantStateName() 5015 + " my state " + getCurrentState().getName()); 5016 } else { 5017 loge("SAVE_NETWORK id=" + Integer.toString(nid) 5018 + " config=" + config.SSID 5019 + " cnid=" + config.networkId 5020 + " supstate=" + mSupplicantStateTracker.getSupplicantStateName() 5021 + " my state " + getCurrentState().getName()); 5022 } 5023 5024 result = mWifiConfigStore.saveNetwork(config); 5025 if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) { 5026 replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED); 5027 if (VDBG) { 5028 loge("Success save network nid=" 5029 + Integer.toString(result.getNetworkId()) 5030 + " autojoin " + mFrameworkAutoJoin.get()); 5031 } 5032 if (mFrameworkAutoJoin.get()) { 5033 /* Tell autojoin the user did try to modify and save that network */ 5034 mWifiAutoJoinController.updateConfigurationHistory(result.getNetworkId() 5035 ,true, false); 5036 mWifiAutoJoinController.attemptAutoJoin(); 5037 } 5038 } else { 5039 loge("Failed to save network"); 5040 replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, 5041 WifiManager.ERROR); 5042 } 5043 break; 5044 case WifiManager.FORGET_NETWORK: 5045 if (mWifiConfigStore.forgetNetwork(message.arg1)) { 5046 replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED); 5047 } else { 5048 loge("Failed to forget network"); 5049 replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED, 5050 WifiManager.ERROR); 5051 } 5052 break; 5053 case WifiManager.START_WPS: 5054 WpsInfo wpsInfo = (WpsInfo) message.obj; 5055 WpsResult wpsResult; 5056 switch (wpsInfo.setup) { 5057 case WpsInfo.PBC: 5058 wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo); 5059 break; 5060 case WpsInfo.KEYPAD: 5061 wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo); 5062 break; 5063 case WpsInfo.DISPLAY: 5064 wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo); 5065 break; 5066 default: 5067 wpsResult = new WpsResult(Status.FAILURE); 5068 loge("Invalid setup for WPS"); 5069 break; 5070 } 5071 mWifiConfigStore.setLastSelectedConfiguration 5072 (WifiConfiguration.INVALID_NETWORK_ID); 5073 if (wpsResult.status == Status.SUCCESS) { 5074 replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult); 5075 transitionTo(mWpsRunningState); 5076 } else { 5077 loge("Failed to start WPS with config " + wpsInfo.toString()); 5078 replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR); 5079 } 5080 break; 5081 case WifiMonitor.NETWORK_CONNECTION_EVENT: 5082 if (DBG) log("Network connection established"); 5083 mLastNetworkId = message.arg1; 5084 mLastBssid = (String) message.obj; 5085 5086 mWifiInfo.setBSSID(mLastBssid); 5087 mWifiInfo.setNetworkId(mLastNetworkId); 5088 /* send event to CM & network change broadcast */ 5089 setNetworkDetailedState(DetailedState.OBTAINING_IPADDR); 5090 sendNetworkStateChangeBroadcast(mLastBssid); 5091 transitionTo(mObtainingIpState); 5092 break; 5093 case WifiMonitor.NETWORK_DISCONNECTION_EVENT: 5094 if (DBG) log("ConnectModeState: Network connection lost "); 5095 handleNetworkDisconnect(); 5096 transitionTo(mDisconnectedState); 5097 break; 5098 default: 5099 return NOT_HANDLED; 5100 } 5101 return HANDLED; 5102 } 5103 } 5104 5105 private class WifiNetworkAgent extends NetworkAgent { 5106 public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni, 5107 NetworkCapabilities nc, LinkProperties lp, int score) { 5108 super(l, c, TAG, ni, nc, lp, score); 5109 } 5110 protected void unwanted() { 5111 // ignore if we're not the current networkAgent. 5112 if (this != mNetworkAgent) return; 5113 // TODO - don't want this network. What to do? 5114 if (DBG) log("WifiNetworkAgent -> Wifi unwanted score " 5115 + Integer.toString(mWifiInfo.score)); 5116 unwantedNetwork(); 5117 } 5118 } 5119 5120 void unwantedNetwork() { 5121 sendMessage(CMD_UNWANTED_NETWORK); 5122 } 5123 5124 class L2ConnectedState extends State { 5125 @Override 5126 public void enter() { 5127 mRssiPollToken++; 5128 if (mEnableRssiPolling) { 5129 sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0); 5130 } 5131 if (mNetworkAgent != null) { 5132 loge("Have NetworkAgent when entering L2Connected"); 5133 setNetworkDetailedState(DetailedState.DISCONNECTED); 5134 } 5135 setNetworkDetailedState(DetailedState.CONNECTING); 5136 mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext, 5137 "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter, 5138 mLinkProperties, 60); 5139 } 5140 5141 @Override 5142 public void exit() { 5143 //This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState 5144 //Bug: 15347363 5145 //For paranoia's sake, call handleNEtworkDisconnect only if BSSID is null or last networkId 5146 //is not invalid. 5147 if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) { 5148 handleNetworkDisconnect(); 5149 } 5150 } 5151 5152 @Override 5153 public boolean processMessage(Message message) { 5154 logStateAndMessage(message, getClass().getSimpleName()); 5155 5156 switch (message.what) { 5157 case DhcpStateMachine.CMD_PRE_DHCP_ACTION: 5158 handlePreDhcpSetup(); 5159 break; 5160 case DhcpStateMachine.CMD_POST_DHCP_ACTION: 5161 handlePostDhcpSetup(); 5162 if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) { 5163 if (DBG) log("DHCP successful"); 5164 handleIPv4Success((DhcpResults) message.obj); 5165 // We advance to mVerifyingLinkState because handleIPv4Success will call 5166 // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL. 5167 } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) { 5168 if (DBG) log("DHCP failed"); 5169 handleIPv4Failure(); 5170 // As above, we transition to mDisconnectingState via updateLinkProperties. 5171 } 5172 break; 5173 case CMD_IP_CONFIGURATION_SUCCESSFUL: 5174 handleSuccessfulIpConfiguration(); 5175 transitionTo(mVerifyingLinkState); 5176 break; 5177 case CMD_IP_CONFIGURATION_LOST: 5178 handleIpConfigurationLost(); 5179 transitionTo(mDisconnectingState); 5180 break; 5181 case CMD_DISCONNECT: 5182 mWifiNative.disconnect(); 5183 transitionTo(mDisconnectingState); 5184 break; 5185 case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST: 5186 if (message.arg1 == 1) { 5187 mWifiNative.disconnect(); 5188 mTemporarilyDisconnectWifi = true; 5189 transitionTo(mDisconnectingState); 5190 } 5191 break; 5192 case CMD_SET_OPERATIONAL_MODE: 5193 if (message.arg1 != CONNECT_MODE) { 5194 sendMessage(CMD_DISCONNECT); 5195 deferMessage(message); 5196 } 5197 break; 5198 case CMD_SET_COUNTRY_CODE: 5199 deferMessage(message); 5200 break; 5201 case CMD_START_SCAN: 5202 if (DBG) { 5203 loge("WifiStateMachine CMD_START_SCAN source " + message.arg1 5204 + " txSuccessRate="+String.format( "%.2f", mWifiInfo.txSuccessRate) 5205 + " rxSuccessRate="+String.format( "%.2f", mWifiInfo.rxSuccessRate) 5206 + " BSSID=" + mTargetRoamBSSID 5207 + " RSSI=" + mWifiInfo.getRssi()); 5208 } 5209 if (message.arg1 == SCAN_ALARM_SOURCE) { 5210 boolean tryFullBandScan = false; 5211 boolean restrictChannelList = false; 5212 long now_ms = System.currentTimeMillis(); 5213 if (mWifiInfo != null) { 5214 if ((now_ms - lastFullBandConnectedTimeMilli) 5215 > fullBandConnectedTimeIntervalMilli) { 5216 if (DBG) { 5217 loge("WifiStateMachine CMD_START_SCAN try full band scan age=" 5218 + Long.toString(now_ms - lastFullBandConnectedTimeMilli) 5219 + " interval=" + fullBandConnectedTimeIntervalMilli); 5220 } 5221 tryFullBandScan = true; 5222 } 5223 5224 //TODO: really tune the logic 5225 //TODO: right now we discriminate only between full band and partial 5226 //TODO: scans however partial scans can have many channels 5227 //TODO: and be almost same as full band 5228 //TODO: also look and exclude broadcast Rx packets 5229 if (mWifiInfo.txSuccessRate > 5 || mWifiInfo.rxSuccessRate > 30) { 5230 // too much traffic at the interface, hence no full band scan 5231 if (DBG) { 5232 loge("WifiStateMachine CMD_START_SCAN " + 5233 "prevent full band scan due to pkt rate"); 5234 } 5235 tryFullBandScan = false; 5236 } 5237 5238 if (mWifiInfo.txSuccessRate > 50 || mWifiInfo.rxSuccessRate > 100) { 5239 // don't scan if lots of packets are being sent 5240 restrictChannelList = true; 5241 if (mAllowScansWithTraffic == 0 5242 /*|| (getCurrentWifiConfiguration().scanResultCache == null 5243 || getCurrentWifiConfiguration().scanResultCache.size() > 4 5244 || mWifiInfo.getRssi() 5245 > (WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD - 5))*/) { 5246 //there is no need to scan because, either the configuration 5247 //has many 5248 //BSSIDs and thus roaming is left to the chip, 5249 //or, 5250 //RSSI is very good and then the potential roaming gain is not 5251 //high enough to outweigh the negative impact on traffic 5252 if (DBG) { 5253 loge("WifiStateMachine CMD_START_SCAN source " + message.arg1 5254 + " ...and ignore scans" 5255 + " tx=" + String.format("%.2f", mWifiInfo.txSuccessRate) 5256 + " rx=" + String.format("%.2f", mWifiInfo.rxSuccessRate)); 5257 } 5258 return HANDLED; 5259 } 5260 } 5261 } 5262 5263 WifiConfiguration currentConfiguration = getCurrentWifiConfiguration(); 5264 if (currentConfiguration != null) { 5265 if (tryFullBandScan) { 5266 lastFullBandConnectedTimeMilli = now_ms; 5267 if (fullBandConnectedTimeIntervalMilli < 20 * 1000) { 5268 //paranoia, make sure interval is not less than 20 seconds 5269 fullBandConnectedTimeIntervalMilli = 20 * 1000; 5270 } 5271 if (fullBandConnectedTimeIntervalMilli 5272 < maxFullBandConnectedTimeIntervalMilli) { 5273 //increase the interval 5274 fullBandConnectedTimeIntervalMilli 5275 = fullBandConnectedTimeIntervalMilli * 3 / 2; 5276 } 5277 handleScanRequest( 5278 WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message); 5279 } else { 5280 HashSet<Integer> channels 5281 = mWifiConfigStore.makeChannelList(currentConfiguration, 5282 ONE_HOUR_MILLI, restrictChannelList); 5283 if (channels != null && channels.size() != 0) { 5284 StringBuilder freqs = new StringBuilder(); 5285 boolean first = true; 5286 for (Integer channel : channels) { 5287 if (!first) 5288 freqs.append(","); 5289 freqs.append(channel.toString()); 5290 first = false; 5291 } 5292 if (DBG) { 5293 loge("WifiStateMachine starting scan with " + freqs); 5294 } 5295 // call wifi native to start the scan 5296 if (startScanNative( 5297 WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, 5298 freqs.toString())) { 5299 // only count battery consumption if scan request is accepted 5300 noteScanStart(SCAN_ALARM_SOURCE, null); 5301 } 5302 } else { 5303 if (DBG) { 5304 loge("WifiStateMachine starting scan, did not find channels"); 5305 } 5306 handleScanRequest( 5307 WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message); 5308 } 5309 } 5310 } 5311 } else { 5312 handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message); 5313 } 5314 break; 5315 /* Ignore connection to same network */ 5316 case WifiManager.CONNECT_NETWORK: 5317 int netId = message.arg1; 5318 if (mWifiInfo.getNetworkId() == netId) { 5319 break; 5320 } 5321 return NOT_HANDLED; 5322 case WifiManager.SAVE_NETWORK: 5323 WifiConfiguration config = (WifiConfiguration) message.obj; 5324 int nid = config.networkId; 5325 if (config == null) { 5326 loge("SAVE_NETWORK-L2 id=" + Integer.toString(nid) 5327 + " " + mSupplicantStateTracker.getSupplicantStateName() 5328 + " my state " + getCurrentState().getName()); 5329 } else { 5330 loge("SAVE_NETWORK-L2 id=" + Integer.toString(nid) 5331 + " SSID=" + config.SSID 5332 + " cnid=" + config.networkId 5333 + " autojoin=" + Integer.toString(config.autoJoinStatus) 5334 + " supstate=" + mSupplicantStateTracker.getSupplicantStateName() 5335 + " my state " + getCurrentState().getName() 5336 + " uid " + Integer.toString(config.creatorUid)); 5337 } 5338 5339 NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config); 5340 if (mWifiInfo.getNetworkId() == result.getNetworkId()) { 5341 if (result.hasIpChanged()) { 5342 log("Reconfiguring IP on connection"); 5343 transitionTo(mObtainingIpState); 5344 } 5345 if (result.hasProxyChanged()) { 5346 log("Reconfiguring proxy on connection"); 5347 updateLinkProperties(); 5348 } 5349 } 5350 5351 if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) { 5352 replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED); 5353 if (VDBG) { 5354 loge("Success save network l2 nid=" 5355 + Integer.toString(result.getNetworkId()) 5356 + " autojoin " + mFrameworkAutoJoin.get()); 5357 } 5358 if (mFrameworkAutoJoin.get()) { 5359 /* Tell autojoin the user did try to modify and save that network */ 5360 mWifiAutoJoinController.updateConfigurationHistory(config.networkId, 5361 true, false); 5362 mWifiAutoJoinController.attemptAutoJoin(); 5363 } 5364 } else { 5365 loge("Failed to save network"); 5366 replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, 5367 WifiManager.ERROR); 5368 } 5369 break; 5370 /* Ignore */ 5371 case WifiMonitor.NETWORK_CONNECTION_EVENT: 5372 break; 5373 case CMD_RSSI_POLL: 5374 if (message.arg1 == mRssiPollToken) { 5375 WifiLinkLayerStats stats = null; 5376 //try a reading L2 stats a couple of time, allow for a few failures 5377 //in case the HAL/drivers are not completely initialized once we get there 5378 if (mWifiLinkLayerStatsSupported > 0) { 5379 stats = mWifiNative.getWifiLinkLayerStats(); 5380 if (stats == null && mWifiLinkLayerStatsSupported > 0) { 5381 mWifiLinkLayerStatsSupported -= 1; 5382 } 5383 } 5384 // Get Info and continue polling 5385 fetchRssiLinkSpeedAndFrequencyNative(); 5386 calculateWifiScore(stats); 5387 sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, 5388 mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS); 5389 } else { 5390 // Polling has completed 5391 } 5392 break; 5393 case CMD_ENABLE_RSSI_POLL: 5394 mEnableRssiPolling = (message.arg1 == 1); 5395 mRssiPollToken++; 5396 if (mEnableRssiPolling) { 5397 // first poll 5398 fetchRssiLinkSpeedAndFrequencyNative(); 5399 sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, 5400 mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS); 5401 } 5402 break; 5403 case WifiManager.RSSI_PKTCNT_FETCH: 5404 RssiPacketCountInfo info = new RssiPacketCountInfo(); 5405 fetchRssiLinkSpeedAndFrequencyNative(); 5406 info.rssi = mWifiInfo.getRssi(); 5407 fetchPktcntNative(info); 5408 replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info); 5409 break; 5410 case CMD_UNWANTED_NETWORK: 5411 //mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo); 5412 //disconnecting is probably too rough and reduce the chance we recover quickly. 5413 //we should not have to disconnect, instead rely on network stack to send data 5414 //traffic somewhere else but remember that this network is roamable with a 5415 //low wifi score threshold 5416 sendMessage(CMD_DISCONNECT); 5417 break; 5418 default: 5419 return NOT_HANDLED; 5420 } 5421 5422 return HANDLED; 5423 } 5424 } 5425 5426 class ObtainingIpState extends State { 5427 @Override 5428 public void enter() { 5429 if (DBG) { 5430 String key = ""; 5431 if (getCurrentWifiConfiguration() != null) { 5432 key = getCurrentWifiConfiguration().configKey(); 5433 } 5434 log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId) 5435 + " " + key + " " 5436 + " roam=" + mAutoRoaming 5437 + " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId)); 5438 } 5439 5440 try { 5441 mNwService.enableIpv6(mInterfaceName); 5442 } catch (RemoteException re) { 5443 loge("Failed to enable IPv6: " + re); 5444 } catch (IllegalStateException e) { 5445 loge("Failed to enable IPv6: " + e); 5446 } 5447 5448 if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) { 5449 // TODO: If we're switching between static IP configuration and DHCP, remove the 5450 // static configuration first. 5451 if (isRoaming()) { 5452 renewDhcp(); 5453 } else { 5454 startDhcp(); 5455 } 5456 } else { 5457 // stop any running dhcp before assigning static IP 5458 stopDhcp(); 5459 DhcpResults dhcpResults = new DhcpResults( 5460 mWifiConfigStore.getLinkProperties(mLastNetworkId)); 5461 InterfaceConfiguration ifcg = new InterfaceConfiguration(); 5462 Iterator<LinkAddress> addrs = 5463 dhcpResults.linkProperties.getLinkAddresses().iterator(); 5464 if (!addrs.hasNext()) { 5465 loge("Static IP lacks address"); 5466 sendMessage(CMD_STATIC_IP_FAILURE); 5467 } else { 5468 ifcg.setLinkAddress(addrs.next()); 5469 ifcg.setInterfaceUp(); 5470 try { 5471 mNwService.setInterfaceConfig(mInterfaceName, ifcg); 5472 if (DBG) log("Static IP configuration succeeded"); 5473 sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults); 5474 } catch (RemoteException re) { 5475 loge("Static IP configuration failed: " + re); 5476 sendMessage(CMD_STATIC_IP_FAILURE); 5477 } catch (IllegalStateException e) { 5478 loge("Static IP configuration failed: " + e); 5479 sendMessage(CMD_STATIC_IP_FAILURE); 5480 } 5481 } 5482 } 5483 } 5484 @Override 5485 public boolean processMessage(Message message) { 5486 logStateAndMessage(message, getClass().getSimpleName()); 5487 5488 switch(message.what) { 5489 case CMD_STATIC_IP_SUCCESS: 5490 handleIPv4Success((DhcpResults) message.obj); 5491 break; 5492 case CMD_STATIC_IP_FAILURE: 5493 handleIPv4Failure(); 5494 break; 5495 case WifiManager.SAVE_NETWORK: 5496 deferMessage(message); 5497 break; 5498 /* Defer any power mode changes since we must keep active power mode at DHCP */ 5499 case CMD_SET_HIGH_PERF_MODE: 5500 deferMessage(message); 5501 break; 5502 /* Defer scan request since we should not switch to other channels at DHCP */ 5503 case CMD_START_SCAN: 5504 deferMessage(message); 5505 break; 5506 default: 5507 return NOT_HANDLED; 5508 } 5509 return HANDLED; 5510 } 5511 } 5512 5513 class VerifyingLinkState extends State { 5514 @Override 5515 public void enter() { 5516 log(getName() + " enter"); 5517 setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK); 5518 mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK); 5519 sendNetworkStateChangeBroadcast(mLastBssid); 5520 mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE; 5521 } 5522 @Override 5523 public boolean processMessage(Message message) { 5524 logStateAndMessage(message, getClass().getSimpleName()); 5525 5526 switch (message.what) { 5527 case WifiWatchdogStateMachine.POOR_LINK_DETECTED: 5528 //stay here 5529 log(getName() + " POOR_LINK_DETECTED: no transition"); 5530 break; 5531 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED: 5532 log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check"); 5533 // Send out a broadcast with the CAPTIVE_PORTAL_CHECK to preserve 5534 // existing behaviour. The captive portal check really happens after we 5535 // transition into DetailedState.CONNECTED. 5536 setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK); 5537 mWifiConfigStore.updateStatus(mLastNetworkId, 5538 DetailedState.CAPTIVE_PORTAL_CHECK); 5539 sendNetworkStateChangeBroadcast(mLastBssid); 5540 5541 log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED"); 5542 setNetworkDetailedState(DetailedState.CONNECTED); 5543 mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED); 5544 sendNetworkStateChangeBroadcast(mLastBssid); 5545 transitionTo(mConnectedState); 5546 5547 break; 5548 case CMD_START_SCAN: 5549 deferMessage(message); 5550 break; 5551 default: 5552 if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED"); 5553 return NOT_HANDLED; 5554 } 5555 return HANDLED; 5556 } 5557 } 5558 5559 class RoamingState extends State { 5560 @Override 5561 public void enter() { 5562 if (DBG) { 5563 log("RoamingState Enter" 5564 + " mScreenOn=" + mScreenOn ); 5565 } 5566 setScanAlarm(false); 5567 //TODO: actually implement an alarm, but so as to disconnect if roaming fails 5568 } 5569 @Override 5570 public boolean processMessage(Message message) { 5571 logStateAndMessage(message, getClass().getSimpleName()); 5572 5573 switch (message.what) { 5574 case WifiWatchdogStateMachine.POOR_LINK_DETECTED: 5575 if (DBG) log("Roaming and Watchdog reports poor link -> ignore"); 5576 return HANDLED; 5577 case CMD_UNWANTED_NETWORK: 5578 if (DBG) log("Roaming and CS doesnt want the network -> ignore"); 5579 return HANDLED; 5580 case CMD_SET_OPERATIONAL_MODE: 5581 if (message.arg1 != CONNECT_MODE) { 5582 deferMessage(message); 5583 } 5584 break; 5585 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 5586 /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT 5587 * we have missed the network disconnection, transition to mDisconnectedState 5588 * and handle the rest of the events there 5589 */ 5590 StateChangeResult stateChangeResult = (StateChangeResult) message.obj; 5591 setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state)); 5592 break; 5593 case WifiMonitor.NETWORK_CONNECTION_EVENT: 5594 if (DBG) log("roaming and Network connection established"); 5595 mLastNetworkId = message.arg1; 5596 mLastBssid = (String) message.obj; 5597 5598 mWifiInfo.setBSSID(mLastBssid); 5599 mWifiInfo.setNetworkId(mLastNetworkId); 5600 mWifiConfigStore.handleBSSIDBlackList(mLastNetworkId, mLastBssid, true); 5601 /* send event to CM & network change broadcast */ 5602 setNetworkDetailedState(DetailedState.OBTAINING_IPADDR); 5603 sendNetworkStateChangeBroadcast(mLastBssid); 5604 transitionTo(mObtainingIpState); 5605 break; 5606 case WifiMonitor.NETWORK_DISCONNECTION_EVENT: 5607 //throw away but only if it correspond to the network we're roaming to 5608 String bssid = (String)message.obj; 5609 if (DBG) { 5610 log("NETWORK_DISCONNECTION_EVENT in roaming state" 5611 + " BSSID=" + bssid ); 5612 } 5613 if (bssid != null && bssid.equals(mTargetRoamBSSID)) { 5614 handleNetworkDisconnect(); 5615 } 5616 break; 5617 case WifiMonitor.SSID_TEMP_DISABLED: 5618 //auth error while roaming 5619 if (message.arg1 == mLastNetworkId) { 5620 loge("DISABLED while roaming nid=" + Integer.toString(mLastNetworkId)); 5621 sendMessage(CMD_DISCONNECT); 5622 transitionTo(mDisconnectingState); 5623 } 5624 return NOT_HANDLED; 5625 case CMD_START_SCAN: 5626 deferMessage(message); 5627 break; 5628 default: 5629 return NOT_HANDLED; 5630 } 5631 return HANDLED; 5632 } 5633 5634 @Override 5635 public void exit() { 5636 loge("WifiStateMachine: Leaving Roaming state"); 5637 } 5638 } 5639 5640 class ConnectedState extends State { 5641 @Override 5642 public void enter() { 5643 String address; 5644 updateDefaultRouteMacAddress(1000); 5645 if (DBG) { 5646 log("ConnectedState Enter autojoin=" + mFrameworkAutoJoin.get() 5647 + " mScreenOn=" + mScreenOn 5648 + " scanperiod=" + Integer.toString(mConnectedScanPeriodMs) ); 5649 } 5650 if (mFrameworkAutoJoin.get() && mScreenOn) { 5651 mCurrentScanAlarmMs = mConnectedScanPeriodMs; 5652 setScanAlarm(true); 5653 } else { 5654 mCurrentScanAlarmMs = 0; 5655 } 5656 registerConnected(); 5657 lastConnectAttempt = 0; 5658 targetWificonfiguration = null; 5659 5660 } 5661 @Override 5662 public boolean processMessage(Message message) { 5663 logStateAndMessage(message, getClass().getSimpleName()); 5664 5665 switch (message.what) { 5666 case WifiWatchdogStateMachine.POOR_LINK_DETECTED: 5667 if (DBG) log("Watchdog reports poor link"); 5668 /* Report a disconnect */ 5669 setNetworkDetailedState(DetailedState.DISCONNECTED); 5670 mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED); 5671 sendNetworkStateChangeBroadcast(mLastBssid); 5672 5673 transitionTo(mVerifyingLinkState); 5674 break; 5675 case CMD_UNWANTED_NETWORK: 5676 mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo); 5677 //disconnecting is probably too rough and reduce the chance we recover quickly. 5678 //we should not have to disconnect, instead rely on network stack to send data 5679 //traffic somewhere else but remember that this network is roamable with a 5680 //low wifi score threshold 5681 sendMessage(CMD_DISCONNECT); 5682 return HANDLED; 5683 case CMD_AUTO_ROAM: 5684 /* this will happen similarly to an Auto_CONNECT, except we specify the BSSID */ 5685 /* Work Around: wpa_supplicant can get in a bad state where it returns a non 5686 * associated status thus the STATUS command but somehow-someplace still thinks 5687 * it is associated and thus will ignore select/reconnect command with 5688 * following message: 5689 * "Already associated with the selected network - do nothing" 5690 * 5691 * Hence, sends a disconnect to supplicant first. 5692 */ 5693 //mWifiNative.disconnect(); 5694 5695 /* connect command coming from auto-join */ 5696 ScanResult candidate = (ScanResult)message.obj; 5697 String bssid = candidate.BSSID; 5698 int netId = mLastNetworkId; 5699 WifiConfiguration config = getCurrentWifiConfiguration(); 5700 5701 loge("CMD_AUTO_ROAM sup state " 5702 + mSupplicantStateTracker.getSupplicantStateName() 5703 + " my state " + getCurrentState().getName() 5704 + " nid=" + Integer.toString(netId) 5705 + " roam=" + Integer.toString(message.arg2) 5706 + bssid); 5707 if (config == null) { 5708 loge("AUTO_ROAM and no config, bail out..."); 5709 break; 5710 } 5711 5712 /* save the BSSID so as to lock it @ firmware */ 5713 autoRoamSetBSSID(config, bssid); 5714 5715 /* Save the network config */ 5716 loge("CMD_AUTO_ROAM will save config -> " + config.SSID 5717 + " nid=" + Integer.toString(netId)); 5718 NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config); 5719 netId = result.getNetworkId(); 5720 loge("CMD_AUTO_ROAM did save config -> " 5721 + " nid=" + Integer.toString(netId)); 5722 5723 if (mWifiConfigStore.selectNetwork(netId) && 5724 mWifiNative.reassociate()) { 5725 lastConnectAttempt = System.currentTimeMillis(); 5726 targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId); 5727 5728 /* The state tracker handles enabling networks upon completion/failure */ 5729 mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK); 5730 //replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED); 5731 mAutoRoaming = message.arg2; 5732 transitionTo(mRoamingState); 5733 5734 } else { 5735 loge("Failed to connect config: " + config + " netId: " + netId); 5736 replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, 5737 WifiManager.ERROR); 5738 break; 5739 } 5740 break; 5741 default: 5742 return NOT_HANDLED; 5743 } 5744 return HANDLED; 5745 } 5746 5747 @Override 5748 public void exit() { 5749 loge("WifiStateMachine: Leaving Connected state"); 5750 setScanAlarm(false); 5751 } 5752 } 5753 5754 class DisconnectingState extends State { 5755 5756 @Override 5757 public void enter() { 5758 if (mFrameworkAutoJoin.get()) { 5759 mCurrentScanAlarmMs = mDisconnectedScanPeriodMs; 5760 } else { 5761 5762 mCurrentScanAlarmMs = mFrameworkScanIntervalMs; 5763 } 5764 5765 if (PDBG) { 5766 loge(" Enter DisconnectingState State scan interval " + mFrameworkScanIntervalMs 5767 + " mEnableBackgroundScan= " + mEnableBackgroundScan 5768 + " screenOn=" + mScreenOn); 5769 } 5770 if (mScreenOn) 5771 setScanAlarm(true); 5772 } 5773 5774 @Override 5775 public boolean processMessage(Message message) { 5776 logStateAndMessage(message, getClass().getSimpleName()); 5777 switch (message.what) { 5778 case CMD_SET_OPERATIONAL_MODE: 5779 if (message.arg1 != CONNECT_MODE) { 5780 deferMessage(message); 5781 } 5782 break; 5783 case CMD_START_SCAN: 5784 //ignore scans while disconnecting 5785 return HANDLED; 5786 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 5787 /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT 5788 * we have missed the network disconnection, transition to mDisconnectedState 5789 * and handle the rest of the events there 5790 */ 5791 deferMessage(message); 5792 handleNetworkDisconnect(); 5793 transitionTo(mDisconnectedState); 5794 break; 5795 default: 5796 return NOT_HANDLED; 5797 } 5798 return HANDLED; 5799 } 5800 5801 @Override 5802 public void exit() { 5803 mCurrentScanAlarmMs = 0; 5804 } 5805 } 5806 5807 class DisconnectedState extends State { 5808 @Override 5809 public void enter() { 5810 // We dont scan frequently if this is a temporary disconnect 5811 // due to p2p 5812 if (mTemporarilyDisconnectWifi) { 5813 mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE); 5814 return; 5815 } 5816 5817 // loose the last selection choice 5818 // mWifiAutoJoinController.setLastSelectedConfiguration 5819 // (WifiConfiguration.INVALID_NETWORK_ID); 5820 5821 mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(), 5822 Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS, 5823 mDefaultFrameworkScanIntervalMs); 5824 5825 if (mFrameworkAutoJoin.get()) { 5826 if (mScreenOn) 5827 mCurrentScanAlarmMs = mDisconnectedScanPeriodMs; 5828 } else { 5829 mCurrentScanAlarmMs = mFrameworkScanIntervalMs; 5830 } 5831 5832 if (PDBG) { 5833 loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs 5834 + " mEnableBackgroundScan= " + mEnableBackgroundScan 5835 + " screenOn=" + mScreenOn); 5836 } 5837 5838 /** clear the roaming state, if we were roaming, we failed */ 5839 mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE; 5840 5841 /* 5842 * mFrameworkAutoJoin is False: We initiate background scanning if it is enabled, 5843 * otherwise we initiate an infrequent scan that wakes up the device to ensure 5844 * a user connects to an access point on the move 5845 * 5846 * mFrameworkAutoJoin is True: 5847 * - screen dark and PNO supported => scan alarm disabled 5848 * - everything else => scan alarm enabled with mDefaultFrameworkScanIntervalMs period 5849 */ 5850 if ((mScreenOn == false) && mEnableBackgroundScan) { //mEnableBackgroundScan) { 5851 /* If a regular scan result is pending, do not initiate background 5852 * scan until the scan results are returned. This is needed because 5853 * initiating a background scan will cancel the regular scan and 5854 * scan results will not be returned until background scanning is 5855 * cleared 5856 */ 5857 if (!mIsScanOngoing) { 5858 mWifiNative.enableBackgroundScan(true); 5859 } 5860 } else { 5861 setScanAlarm(true); 5862 } 5863 5864 if (mFrameworkAutoJoin.get() && mScreenOn && isScanAllowed()) { 5865 startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null); 5866 } 5867 5868 /** 5869 * If we have no networks saved, the supplicant stops doing the periodic scan. 5870 * The scans are useful to notify the user of the presence of an open network. 5871 * Note that these are not wake up scans. 5872 */ 5873 if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) { 5874 sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN, 5875 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs); 5876 } 5877 } 5878 @Override 5879 public boolean processMessage(Message message) { 5880 boolean ret = HANDLED; 5881 5882 logStateAndMessage(message, getClass().getSimpleName()); 5883 5884 switch (message.what) { 5885 case CMD_NO_NETWORKS_PERIODIC_SCAN: 5886 if (mP2pConnected.get()) break; 5887 if (message.arg1 == mPeriodicScanToken && 5888 mWifiConfigStore.getConfiguredNetworks().size() == 0) { 5889 startScan(UNKNOWN_SCAN_SOURCE, null, null); 5890 sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN, 5891 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs); 5892 } 5893 break; 5894 case WifiManager.FORGET_NETWORK: 5895 case CMD_REMOVE_NETWORK: 5896 // Set up a delayed message here. After the forget/remove is handled 5897 // the handled delayed message will determine if there is a need to 5898 // scan and continue 5899 sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN, 5900 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs); 5901 ret = NOT_HANDLED; 5902 break; 5903 case CMD_SET_OPERATIONAL_MODE: 5904 if (message.arg1 != CONNECT_MODE) { 5905 mOperationalMode = message.arg1; 5906 5907 mWifiConfigStore.disableAllNetworks(); 5908 if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) { 5909 mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ); 5910 setWifiState(WIFI_STATE_DISABLED); 5911 } 5912 5913 transitionTo(mScanModeState); 5914 } 5915 break; 5916 case CMD_ENABLE_BACKGROUND_SCAN: 5917 mEnableBackgroundScan = (message.arg1 == 1); 5918 loge("enableBackgroundScanCommand enabled=" + mEnableBackgroundScan 5919 + " suppState:" + mSupplicantStateTracker.getSupplicantStateName()); 5920 5921 if (mEnableBackgroundScan) { 5922 mWifiNative.enableBackgroundScan(true); 5923 setScanAlarm(false); 5924 } else { 5925 if (mFrameworkAutoJoin.get()) { 5926 // tell supplicant to disconnect so as it doesnt start scanning 5927 // for connection upon disabling background scan 5928 mWifiNative.disconnect(); 5929 } 5930 mWifiNative.enableBackgroundScan(false); 5931 setScanAlarm(true); 5932 } 5933 break; 5934 /* Ignore network disconnect */ 5935 case WifiMonitor.NETWORK_DISCONNECTION_EVENT: 5936 break; 5937 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 5938 StateChangeResult stateChangeResult = (StateChangeResult) message.obj; 5939 setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state)); 5940 /* ConnectModeState does the rest of the handling */ 5941 ret = NOT_HANDLED; 5942 break; 5943 case CMD_START_SCAN: 5944 if (!isScanAllowed()) { 5945 //ignore the scan request 5946 return HANDLED; 5947 } 5948 /* Disable background scan temporarily during a regular scan */ 5949 if (mEnableBackgroundScan) { 5950 mWifiNative.enableBackgroundScan(false); 5951 } 5952 /* Handled in parent state */ 5953 ret = NOT_HANDLED; 5954 break; 5955 case WifiMonitor.SCAN_RESULTS_EVENT: 5956 /* Re-enable background scan when a pending scan result is received */ 5957 if (mEnableBackgroundScan && mIsScanOngoing) { 5958 mWifiNative.enableBackgroundScan(true); 5959 } 5960 /* Handled in parent state */ 5961 ret = NOT_HANDLED; 5962 break; 5963 case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED: 5964 NetworkInfo info = (NetworkInfo) message.obj; 5965 mP2pConnected.set(info.isConnected()); 5966 if (mP2pConnected.get()) { 5967 int defaultInterval = mContext.getResources().getInteger( 5968 R.integer.config_wifi_scan_interval_p2p_connected); 5969 long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(), 5970 Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS, 5971 defaultInterval); 5972 mWifiNative.setScanInterval((int) scanIntervalMs/1000); 5973 } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) { 5974 if (DBG) log("Turn on scanning after p2p disconnected"); 5975 sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN, 5976 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs); 5977 } 5978 case CMD_RECONNECT: 5979 case CMD_REASSOCIATE: 5980 if (mTemporarilyDisconnectWifi) { 5981 // Drop a third party reconnect/reassociate if STA is 5982 // temporarily disconnected for p2p 5983 break; 5984 } else { 5985 // ConnectModeState handles it 5986 ret = NOT_HANDLED; 5987 } 5988 break; 5989 default: 5990 ret = NOT_HANDLED; 5991 } 5992 return ret; 5993 } 5994 5995 @Override 5996 public void exit() { 5997 /* No need for a background scan upon exit from a disconnected state */ 5998 if (mEnableBackgroundScan) { 5999 mWifiNative.enableBackgroundScan(false); 6000 } 6001 mCurrentScanAlarmMs = 0; 6002 setScanAlarm(false); 6003 } 6004 } 6005 6006 class WpsRunningState extends State { 6007 //Tracks the source to provide a reply 6008 private Message mSourceMessage; 6009 @Override 6010 public void enter() { 6011 mSourceMessage = Message.obtain(getCurrentMessage()); 6012 } 6013 @Override 6014 public boolean processMessage(Message message) { 6015 logStateAndMessage(message, getClass().getSimpleName()); 6016 6017 switch (message.what) { 6018 case WifiMonitor.WPS_SUCCESS_EVENT: 6019 // Ignore intermediate success, wait for full connection 6020 break; 6021 case WifiMonitor.NETWORK_CONNECTION_EVENT: 6022 replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED); 6023 mSourceMessage.recycle(); 6024 mSourceMessage = null; 6025 deferMessage(message); 6026 transitionTo(mDisconnectedState); 6027 break; 6028 case WifiMonitor.WPS_OVERLAP_EVENT: 6029 replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, 6030 WifiManager.WPS_OVERLAP_ERROR); 6031 mSourceMessage.recycle(); 6032 mSourceMessage = null; 6033 transitionTo(mDisconnectedState); 6034 break; 6035 case WifiMonitor.WPS_FAIL_EVENT: 6036 //arg1 has the reason for the failure 6037 if ((message.arg1 != WifiManager.ERROR) || (message.arg2 != 0)) { 6038 replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1); 6039 mSourceMessage.recycle(); 6040 mSourceMessage = null; 6041 transitionTo(mDisconnectedState); 6042 } else { 6043 if (DBG) log("Ignore unspecified fail event during WPS connection"); 6044 } 6045 break; 6046 case WifiMonitor.WPS_TIMEOUT_EVENT: 6047 replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, 6048 WifiManager.WPS_TIMED_OUT); 6049 mSourceMessage.recycle(); 6050 mSourceMessage = null; 6051 transitionTo(mDisconnectedState); 6052 break; 6053 case WifiManager.START_WPS: 6054 replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS); 6055 break; 6056 case WifiManager.CANCEL_WPS: 6057 if (mWifiNative.cancelWps()) { 6058 replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED); 6059 } else { 6060 replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR); 6061 } 6062 transitionTo(mDisconnectedState); 6063 break; 6064 /* Defer all commands that can cause connections to a different network 6065 * or put the state machine out of connect mode 6066 */ 6067 case CMD_STOP_DRIVER: 6068 case CMD_SET_OPERATIONAL_MODE: 6069 case WifiManager.CONNECT_NETWORK: 6070 case CMD_ENABLE_NETWORK: 6071 case CMD_RECONNECT: 6072 case CMD_REASSOCIATE: 6073 deferMessage(message); 6074 break; 6075 case CMD_AUTO_CONNECT: 6076 case CMD_AUTO_ROAM: 6077 case CMD_START_SCAN: 6078 return HANDLED; 6079 case WifiMonitor.NETWORK_DISCONNECTION_EVENT: 6080 if (DBG) log("Network connection lost"); 6081 handleNetworkDisconnect(); 6082 break; 6083 case WifiMonitor.ASSOCIATION_REJECTION_EVENT: 6084 if (DBG) log("Ignore Assoc reject event during WPS Connection"); 6085 break; 6086 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: 6087 // Disregard auth failure events during WPS connection. The 6088 // EAP sequence is retried several times, and there might be 6089 // failures (especially for wps pin). We will get a WPS_XXX 6090 // event at the end of the sequence anyway. 6091 if (DBG) log("Ignore auth failure during WPS connection"); 6092 break; 6093 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: 6094 //Throw away supplicant state changes when WPS is running. 6095 //We will start getting supplicant state changes once we get 6096 //a WPS success or failure 6097 break; 6098 default: 6099 return NOT_HANDLED; 6100 } 6101 return HANDLED; 6102 } 6103 6104 @Override 6105 public void exit() { 6106 mWifiConfigStore.enableAllNetworks(); 6107 mWifiConfigStore.loadConfiguredNetworks(); 6108 } 6109 } 6110 6111 class SoftApStartingState extends State { 6112 @Override 6113 public void enter() { 6114 final Message message = getCurrentMessage(); 6115 if (message.what == CMD_START_AP) { 6116 final WifiConfiguration config = (WifiConfiguration) message.obj; 6117 6118 if (config == null) { 6119 mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG); 6120 } else { 6121 mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config); 6122 startSoftApWithConfig(config); 6123 } 6124 } else { 6125 throw new RuntimeException("Illegal transition to SoftApStartingState: " + message); 6126 } 6127 } 6128 @Override 6129 public boolean processMessage(Message message) { 6130 logStateAndMessage(message, getClass().getSimpleName()); 6131 6132 switch(message.what) { 6133 case CMD_START_SUPPLICANT: 6134 case CMD_STOP_SUPPLICANT: 6135 case CMD_START_AP: 6136 case CMD_STOP_AP: 6137 case CMD_START_DRIVER: 6138 case CMD_STOP_DRIVER: 6139 case CMD_SET_OPERATIONAL_MODE: 6140 case CMD_SET_COUNTRY_CODE: 6141 case CMD_SET_FREQUENCY_BAND: 6142 case CMD_START_PACKET_FILTERING: 6143 case CMD_STOP_PACKET_FILTERING: 6144 case CMD_TETHER_STATE_CHANGE: 6145 deferMessage(message); 6146 break; 6147 case WifiStateMachine.CMD_RESPONSE_AP_CONFIG: 6148 WifiConfiguration config = (WifiConfiguration) message.obj; 6149 if (config != null) { 6150 startSoftApWithConfig(config); 6151 } else { 6152 loge("Softap config is null!"); 6153 sendMessage(CMD_START_AP_FAILURE); 6154 } 6155 break; 6156 case CMD_START_AP_SUCCESS: 6157 setWifiApState(WIFI_AP_STATE_ENABLED); 6158 transitionTo(mSoftApStartedState); 6159 break; 6160 case CMD_START_AP_FAILURE: 6161 setWifiApState(WIFI_AP_STATE_FAILED); 6162 transitionTo(mInitialState); 6163 break; 6164 default: 6165 return NOT_HANDLED; 6166 } 6167 return HANDLED; 6168 } 6169 } 6170 6171 class SoftApStartedState extends State { 6172 @Override 6173 public boolean processMessage(Message message) { 6174 logStateAndMessage(message, getClass().getSimpleName()); 6175 6176 switch(message.what) { 6177 case CMD_STOP_AP: 6178 if (DBG) log("Stopping Soft AP"); 6179 /* We have not tethered at this point, so we just shutdown soft Ap */ 6180 try { 6181 mNwService.stopAccessPoint(mInterfaceName); 6182 } catch(Exception e) { 6183 loge("Exception in stopAccessPoint()"); 6184 } 6185 setWifiApState(WIFI_AP_STATE_DISABLED); 6186 transitionTo(mInitialState); 6187 break; 6188 case CMD_START_AP: 6189 // Ignore a start on a running access point 6190 break; 6191 /* Fail client mode operation when soft AP is enabled */ 6192 case CMD_START_SUPPLICANT: 6193 loge("Cannot start supplicant with a running soft AP"); 6194 setWifiState(WIFI_STATE_UNKNOWN); 6195 break; 6196 case CMD_TETHER_STATE_CHANGE: 6197 TetherStateChange stateChange = (TetherStateChange) message.obj; 6198 if (startTethering(stateChange.available)) { 6199 transitionTo(mTetheringState); 6200 } 6201 break; 6202 default: 6203 return NOT_HANDLED; 6204 } 6205 return HANDLED; 6206 } 6207 } 6208 6209 class TetheringState extends State { 6210 @Override 6211 public void enter() { 6212 /* Send ourselves a delayed message to shut down if tethering fails to notify */ 6213 sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT, 6214 ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS); 6215 } 6216 @Override 6217 public boolean processMessage(Message message) { 6218 logStateAndMessage(message, getClass().getSimpleName()); 6219 6220 switch(message.what) { 6221 case CMD_TETHER_STATE_CHANGE: 6222 TetherStateChange stateChange = (TetherStateChange) message.obj; 6223 if (isWifiTethered(stateChange.active)) { 6224 transitionTo(mTetheredState); 6225 } 6226 return HANDLED; 6227 case CMD_TETHER_NOTIFICATION_TIMED_OUT: 6228 if (message.arg1 == mTetherToken) { 6229 loge("Failed to get tether update, shutdown soft access point"); 6230 transitionTo(mSoftApStartedState); 6231 // Needs to be first thing handled 6232 sendMessageAtFrontOfQueue(CMD_STOP_AP); 6233 } 6234 break; 6235 case CMD_START_SUPPLICANT: 6236 case CMD_STOP_SUPPLICANT: 6237 case CMD_START_AP: 6238 case CMD_STOP_AP: 6239 case CMD_START_DRIVER: 6240 case CMD_STOP_DRIVER: 6241 case CMD_SET_OPERATIONAL_MODE: 6242 case CMD_SET_COUNTRY_CODE: 6243 case CMD_SET_FREQUENCY_BAND: 6244 case CMD_START_PACKET_FILTERING: 6245 case CMD_STOP_PACKET_FILTERING: 6246 deferMessage(message); 6247 break; 6248 default: 6249 return NOT_HANDLED; 6250 } 6251 return HANDLED; 6252 } 6253 } 6254 6255 class TetheredState extends State { 6256 @Override 6257 public boolean processMessage(Message message) { 6258 logStateAndMessage(message, getClass().getSimpleName()); 6259 6260 switch(message.what) { 6261 case CMD_TETHER_STATE_CHANGE: 6262 TetherStateChange stateChange = (TetherStateChange) message.obj; 6263 if (!isWifiTethered(stateChange.active)) { 6264 loge("Tethering reports wifi as untethered!, shut down soft Ap"); 6265 setHostApRunning(null, false); 6266 setHostApRunning(null, true); 6267 } 6268 return HANDLED; 6269 case CMD_STOP_AP: 6270 if (DBG) log("Untethering before stopping AP"); 6271 setWifiApState(WIFI_AP_STATE_DISABLING); 6272 stopTethering(); 6273 transitionTo(mUntetheringState); 6274 // More work to do after untethering 6275 deferMessage(message); 6276 break; 6277 default: 6278 return NOT_HANDLED; 6279 } 6280 return HANDLED; 6281 } 6282 } 6283 6284 class UntetheringState extends State { 6285 @Override 6286 public void enter() { 6287 /* Send ourselves a delayed message to shut down if tethering fails to notify */ 6288 sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT, 6289 ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS); 6290 6291 } 6292 @Override 6293 public boolean processMessage(Message message) { 6294 logStateAndMessage(message, getClass().getSimpleName()); 6295 6296 switch(message.what) { 6297 case CMD_TETHER_STATE_CHANGE: 6298 TetherStateChange stateChange = (TetherStateChange) message.obj; 6299 6300 /* Wait till wifi is untethered */ 6301 if (isWifiTethered(stateChange.active)) break; 6302 6303 transitionTo(mSoftApStartedState); 6304 break; 6305 case CMD_TETHER_NOTIFICATION_TIMED_OUT: 6306 if (message.arg1 == mTetherToken) { 6307 loge("Failed to get tether update, force stop access point"); 6308 transitionTo(mSoftApStartedState); 6309 } 6310 break; 6311 case CMD_START_SUPPLICANT: 6312 case CMD_STOP_SUPPLICANT: 6313 case CMD_START_AP: 6314 case CMD_STOP_AP: 6315 case CMD_START_DRIVER: 6316 case CMD_STOP_DRIVER: 6317 case CMD_SET_OPERATIONAL_MODE: 6318 case CMD_SET_COUNTRY_CODE: 6319 case CMD_SET_FREQUENCY_BAND: 6320 case CMD_START_PACKET_FILTERING: 6321 case CMD_STOP_PACKET_FILTERING: 6322 deferMessage(message); 6323 break; 6324 default: 6325 return NOT_HANDLED; 6326 } 6327 return HANDLED; 6328 } 6329 } 6330 6331 //State machine initiated requests can have replyTo set to null indicating 6332 //there are no recepients, we ignore those reply actions 6333 private void replyToMessage(Message msg, int what) { 6334 if (msg.replyTo == null) return; 6335 Message dstMsg = obtainMessageWithArg2(msg); 6336 dstMsg.what = what; 6337 mReplyChannel.replyToMessage(msg, dstMsg); 6338 } 6339 6340 private void replyToMessage(Message msg, int what, int arg1) { 6341 if (msg.replyTo == null) return; 6342 Message dstMsg = obtainMessageWithArg2(msg); 6343 dstMsg.what = what; 6344 dstMsg.arg1 = arg1; 6345 mReplyChannel.replyToMessage(msg, dstMsg); 6346 } 6347 6348 private void replyToMessage(Message msg, int what, Object obj) { 6349 if (msg.replyTo == null) return; 6350 Message dstMsg = obtainMessageWithArg2(msg); 6351 dstMsg.what = what; 6352 dstMsg.obj = obj; 6353 mReplyChannel.replyToMessage(msg, dstMsg); 6354 } 6355 6356 /** 6357 * arg2 on the source message has a unique id that needs to be retained in replies 6358 * to match the request 6359 6360 * see WifiManager for details 6361 */ 6362 private Message obtainMessageWithArg2(Message srcMsg) { 6363 Message msg = Message.obtain(); 6364 msg.arg2 = srcMsg.arg2; 6365 return msg; 6366 } 6367} 6368