Home | History | Annotate | Download | only in wifi
      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 
     17 package android.net.wifi;
     18 
     19 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
     20 import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
     21 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
     22 import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
     23 import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
     24 
     25 /**
     26  * TODO:
     27  * Deprecate WIFI_STATE_UNKNOWN
     28  */
     29 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
     30 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
     31 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
     32 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
     33 import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
     34 
     35 import android.app.AlarmManager;
     36 import android.app.PendingIntent;
     37 import android.app.backup.IBackupManager;
     38 import android.bluetooth.BluetoothAdapter;
     39 import android.content.BroadcastReceiver;
     40 import android.content.Context;
     41 import android.content.Intent;
     42 import android.content.IntentFilter;
     43 import android.content.pm.PackageManager;
     44 import android.database.ContentObserver;
     45 import android.net.ConnectivityManager;
     46 import android.net.DhcpResults;
     47 import android.net.DhcpStateMachine;
     48 import android.net.InterfaceConfiguration;
     49 import android.net.LinkAddress;
     50 import android.net.LinkProperties;
     51 import android.net.NetworkInfo;
     52 import android.net.NetworkInfo.DetailedState;
     53 import android.net.NetworkUtils;
     54 import android.net.RouteInfo;
     55 import android.net.wifi.WpsResult.Status;
     56 import android.net.wifi.p2p.WifiP2pManager;
     57 import android.net.wifi.p2p.WifiP2pService;
     58 import android.os.BatteryStats;
     59 import android.os.Binder;
     60 import android.os.IBinder;
     61 import android.os.INetworkManagementService;
     62 import android.os.Message;
     63 import android.os.Messenger;
     64 import android.os.PowerManager;
     65 import android.os.Process;
     66 import android.os.RemoteException;
     67 import android.os.ServiceManager;
     68 import android.os.SystemClock;
     69 import android.os.SystemProperties;
     70 import android.os.UserHandle;
     71 import android.os.WorkSource;
     72 import android.provider.Settings;
     73 import android.util.Log;
     74 import android.util.LruCache;
     75 import android.text.TextUtils;
     76 
     77 import com.android.internal.R;
     78 import com.android.internal.app.IBatteryStats;
     79 import com.android.internal.util.AsyncChannel;
     80 import com.android.internal.util.Protocol;
     81 import com.android.internal.util.State;
     82 import com.android.internal.util.StateMachine;
     83 
     84 import com.android.server.net.BaseNetworkObserver;
     85 
     86 import java.io.FileDescriptor;
     87 import java.io.PrintWriter;
     88 import java.net.InetAddress;
     89 import java.net.Inet6Address;
     90 import java.util.ArrayList;
     91 import java.util.List;
     92 import java.util.Locale;
     93 import java.util.concurrent.atomic.AtomicInteger;
     94 import java.util.concurrent.atomic.AtomicBoolean;
     95 import java.util.Iterator;
     96 import java.util.regex.Pattern;
     97 
     98 /**
     99  * Track the state of Wifi connectivity. All event handling is done here,
    100  * and all changes in connectivity state are initiated here.
    101  *
    102  * Wi-Fi now supports three modes of operation: Client, SoftAp and p2p
    103  * In the current implementation, we support concurrent wifi p2p and wifi operation.
    104  * The WifiStateMachine handles SoftAp and Client operations while WifiP2pService
    105  * handles p2p operation.
    106  *
    107  * @hide
    108  */
    109 public class WifiStateMachine extends StateMachine {
    110 
    111     private static final String NETWORKTYPE = "WIFI";
    112     private static final boolean DBG = false;
    113 
    114     private WifiMonitor mWifiMonitor;
    115     private WifiNative mWifiNative;
    116     private WifiConfigStore mWifiConfigStore;
    117     private INetworkManagementService mNwService;
    118     private ConnectivityManager mCm;
    119 
    120     private final boolean mP2pSupported;
    121     private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
    122     private boolean mTemporarilyDisconnectWifi = false;
    123     private final String mPrimaryDeviceType;
    124 
    125     /* Scan results handling */
    126     private List<ScanResult> mScanResults = new ArrayList<ScanResult>();
    127     private static final Pattern scanResultPattern = Pattern.compile("\t+");
    128     private static final int SCAN_RESULT_CACHE_SIZE = 80;
    129     private final LruCache<String, ScanResult> mScanResultCache;
    130 
    131     /* Batch scan results */
    132     private final List<BatchedScanResult> mBatchedScanResults =
    133             new ArrayList<BatchedScanResult>();
    134     private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE;
    135     private int mExpectedBatchedScans = 0;
    136     private long mBatchedScanMinPollTime = 0;
    137 
    138     /* Chipset supports background scan */
    139     private final boolean mBackgroundScanSupported;
    140 
    141     private String mInterfaceName;
    142     /* Tethering interface could be separate from wlan interface */
    143     private String mTetherInterfaceName;
    144 
    145     private int mLastSignalLevel = -1;
    146     private String mLastBssid;
    147     private int mLastNetworkId;
    148     private boolean mEnableRssiPolling = false;
    149     private boolean mEnableBackgroundScan = false;
    150     private int mRssiPollToken = 0;
    151     private int mReconnectCount = 0;
    152     /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
    153     * In CONNECT_MODE, the STA can scan and connect to an access point
    154     * In SCAN_ONLY_MODE, the STA can only scan for access points
    155     * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off
    156     */
    157     private int mOperationalMode = CONNECT_MODE;
    158     private boolean mScanResultIsPending = false;
    159     private WorkSource mScanWorkSource = null;
    160     private static final int UNKNOWN_SCAN_SOURCE = -1;
    161     /* Tracks if state machine has received any screen state change broadcast yet.
    162      * We can miss one of these at boot.
    163      */
    164     private AtomicBoolean mScreenBroadcastReceived = new AtomicBoolean(false);
    165 
    166     private boolean mBluetoothConnectionActive = false;
    167 
    168     private PowerManager.WakeLock mSuspendWakeLock;
    169 
    170     /**
    171      * Interval in milliseconds between polling for RSSI
    172      * and linkspeed information
    173      */
    174     private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
    175 
    176     /**
    177      * Delay between supplicant restarts upon failure to establish connection
    178      */
    179     private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
    180 
    181     /**
    182      * Number of times we attempt to restart supplicant
    183      */
    184     private static final int SUPPLICANT_RESTART_TRIES = 5;
    185 
    186     private int mSupplicantRestartCount = 0;
    187     /* Tracks sequence number on stop failure message */
    188     private int mSupplicantStopFailureToken = 0;
    189 
    190     /**
    191      * Tether state change notification time out
    192      */
    193     private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
    194 
    195     /* Tracks sequence number on a tether notification time out */
    196     private int mTetherToken = 0;
    197 
    198     /**
    199      * Driver start time out.
    200      */
    201     private static final int DRIVER_START_TIME_OUT_MSECS = 10000;
    202 
    203     /* Tracks sequence number on a driver time out */
    204     private int mDriverStartToken = 0;
    205 
    206     /**
    207      * The link properties of the wifi interface.
    208      * Do not modify this directly; use updateLinkProperties instead.
    209      */
    210     private LinkProperties mLinkProperties;
    211 
    212     /**
    213      * Subset of link properties coming from netlink.
    214      * Currently includes IPv4 and IPv6 addresses. In the future will also include IPv6 DNS servers
    215      * and domains obtained from router advertisements (RFC 6106).
    216      */
    217     private final LinkProperties mNetlinkLinkProperties;
    218 
    219     /* Tracks sequence number on a periodic scan message */
    220     private int mPeriodicScanToken = 0;
    221 
    222     // Wakelock held during wifi start/stop and driver load/unload
    223     private PowerManager.WakeLock mWakeLock;
    224 
    225     private Context mContext;
    226 
    227     private final Object mDhcpResultsLock = new Object();
    228     private DhcpResults mDhcpResults;
    229     private WifiInfo mWifiInfo;
    230     private NetworkInfo mNetworkInfo;
    231     private SupplicantStateTracker mSupplicantStateTracker;
    232     private DhcpStateMachine mDhcpStateMachine;
    233     private boolean mDhcpActive = false;
    234 
    235     private class InterfaceObserver extends BaseNetworkObserver {
    236         private WifiStateMachine mWifiStateMachine;
    237 
    238         InterfaceObserver(WifiStateMachine wifiStateMachine) {
    239             super();
    240             mWifiStateMachine = wifiStateMachine;
    241         }
    242 
    243         @Override
    244         public void addressUpdated(String address, String iface, int flags, int scope) {
    245             if (mWifiStateMachine.mInterfaceName.equals(iface)) {
    246                 if (DBG) {
    247                     log("addressUpdated: " + address + " on " + iface +
    248                         " flags " + flags + " scope " + scope);
    249                 }
    250                 mWifiStateMachine.sendMessage(CMD_IP_ADDRESS_UPDATED, new LinkAddress(address));
    251             }
    252         }
    253 
    254         @Override
    255         public void addressRemoved(String address, String iface, int flags, int scope) {
    256             if (mWifiStateMachine.mInterfaceName.equals(iface)) {
    257                 if (DBG) {
    258                     log("addressRemoved: " + address + " on " + iface +
    259                         " flags " + flags + " scope " + scope);
    260                 }
    261                 mWifiStateMachine.sendMessage(CMD_IP_ADDRESS_REMOVED, new LinkAddress(address));
    262             }
    263         }
    264     }
    265 
    266     private InterfaceObserver mInterfaceObserver;
    267 
    268     private AlarmManager mAlarmManager;
    269     private PendingIntent mScanIntent;
    270     private PendingIntent mDriverStopIntent;
    271     private PendingIntent mBatchedScanIntervalIntent;
    272 
    273     /* Tracks current frequency mode */
    274     private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
    275 
    276     /* Tracks if we are filtering Multicast v4 packets. Default is to filter. */
    277     private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true);
    278 
    279     // Channel for sending replies.
    280     private AsyncChannel mReplyChannel = new AsyncChannel();
    281 
    282     private WifiP2pManager mWifiP2pManager;
    283     //Used to initiate a connection with WifiP2pService
    284     private AsyncChannel mWifiP2pChannel;
    285     private AsyncChannel mWifiApConfigChannel;
    286 
    287     /* The base for wifi message types */
    288     static final int BASE = Protocol.BASE_WIFI;
    289     /* Start the supplicant */
    290     static final int CMD_START_SUPPLICANT                 = BASE + 11;
    291     /* Stop the supplicant */
    292     static final int CMD_STOP_SUPPLICANT                  = BASE + 12;
    293     /* Start the driver */
    294     static final int CMD_START_DRIVER                     = BASE + 13;
    295     /* Stop the driver */
    296     static final int CMD_STOP_DRIVER                      = BASE + 14;
    297     /* Indicates Static IP succeeded */
    298     static final int CMD_STATIC_IP_SUCCESS                = BASE + 15;
    299     /* Indicates Static IP failed */
    300     static final int CMD_STATIC_IP_FAILURE                = BASE + 16;
    301     /* Indicates supplicant stop failed */
    302     static final int CMD_STOP_SUPPLICANT_FAILED           = BASE + 17;
    303     /* Delayed stop to avoid shutting down driver too quick*/
    304     static final int CMD_DELAYED_STOP_DRIVER              = BASE + 18;
    305     /* A delayed message sent to start driver when it fail to come up */
    306     static final int CMD_DRIVER_START_TIMED_OUT           = BASE + 19;
    307     /* Ready to switch to network as default */
    308     static final int CMD_CAPTIVE_CHECK_COMPLETE           = BASE + 20;
    309 
    310     /* Start the soft access point */
    311     static final int CMD_START_AP                         = BASE + 21;
    312     /* Indicates soft ap start succeeded */
    313     static final int CMD_START_AP_SUCCESS                 = BASE + 22;
    314     /* Indicates soft ap start failed */
    315     static final int CMD_START_AP_FAILURE                 = BASE + 23;
    316     /* Stop the soft access point */
    317     static final int CMD_STOP_AP                          = BASE + 24;
    318     /* Set the soft access point configuration */
    319     static final int CMD_SET_AP_CONFIG                    = BASE + 25;
    320     /* Soft access point configuration set completed */
    321     static final int CMD_SET_AP_CONFIG_COMPLETED          = BASE + 26;
    322     /* Request the soft access point configuration */
    323     static final int CMD_REQUEST_AP_CONFIG                = BASE + 27;
    324     /* Response to access point configuration request */
    325     static final int CMD_RESPONSE_AP_CONFIG               = BASE + 28;
    326     /* Invoked when getting a tether state change notification */
    327     static final int CMD_TETHER_STATE_CHANGE              = BASE + 29;
    328     /* A delayed message sent to indicate tether state change failed to arrive */
    329     static final int CMD_TETHER_NOTIFICATION_TIMED_OUT    = BASE + 30;
    330 
    331     static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 31;
    332 
    333     /* Supplicant commands */
    334     /* Is supplicant alive ? */
    335     static final int CMD_PING_SUPPLICANT                  = BASE + 51;
    336     /* Add/update a network configuration */
    337     static final int CMD_ADD_OR_UPDATE_NETWORK            = BASE + 52;
    338     /* Delete a network */
    339     static final int CMD_REMOVE_NETWORK                   = BASE + 53;
    340     /* Enable a network. The device will attempt a connection to the given network. */
    341     static final int CMD_ENABLE_NETWORK                   = BASE + 54;
    342     /* Enable all networks */
    343     static final int CMD_ENABLE_ALL_NETWORKS              = BASE + 55;
    344     /* Blacklist network. De-prioritizes the given BSSID for connection. */
    345     static final int CMD_BLACKLIST_NETWORK                = BASE + 56;
    346     /* Clear the blacklist network list */
    347     static final int CMD_CLEAR_BLACKLIST                  = BASE + 57;
    348     /* Save configuration */
    349     static final int CMD_SAVE_CONFIG                      = BASE + 58;
    350     /* Get configured networks*/
    351     static final int CMD_GET_CONFIGURED_NETWORKS          = BASE + 59;
    352 
    353     /* Supplicant commands after driver start*/
    354     /* Initiate a scan */
    355     static final int CMD_START_SCAN                       = BASE + 71;
    356     /* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
    357     static final int CMD_SET_OPERATIONAL_MODE             = BASE + 72;
    358     /* Disconnect from a network */
    359     static final int CMD_DISCONNECT                       = BASE + 73;
    360     /* Reconnect to a network */
    361     static final int CMD_RECONNECT                        = BASE + 74;
    362     /* Reassociate to a network */
    363     static final int CMD_REASSOCIATE                      = BASE + 75;
    364     /* Controls suspend mode optimizations
    365      *
    366      * When high perf mode is enabled, suspend mode optimizations are disabled
    367      *
    368      * When high perf mode is disabled, suspend mode optimizations are enabled
    369      *
    370      * Suspend mode optimizations include:
    371      * - packet filtering
    372      * - turn off roaming
    373      * - DTIM wake up settings
    374      */
    375     static final int CMD_SET_HIGH_PERF_MODE               = BASE + 77;
    376     /* Set the country code */
    377     static final int CMD_SET_COUNTRY_CODE                 = BASE + 80;
    378     /* Enables RSSI poll */
    379     static final int CMD_ENABLE_RSSI_POLL                 = BASE + 82;
    380     /* RSSI poll */
    381     static final int CMD_RSSI_POLL                        = BASE + 83;
    382     /* Set up packet filtering */
    383     static final int CMD_START_PACKET_FILTERING           = BASE + 84;
    384     /* Clear packet filter */
    385     static final int CMD_STOP_PACKET_FILTERING            = BASE + 85;
    386     /* Enable suspend mode optimizations in the driver */
    387     static final int CMD_SET_SUSPEND_OPT_ENABLED          = BASE + 86;
    388     /* When there are no saved networks, we do a periodic scan to notify user of
    389      * an open network */
    390     static final int CMD_NO_NETWORKS_PERIODIC_SCAN        = BASE + 88;
    391 
    392     /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
    393     static final int MULTICAST_V6  = 1;
    394     static final int MULTICAST_V4  = 0;
    395 
    396    /* Set the frequency band */
    397     static final int CMD_SET_FREQUENCY_BAND               = BASE + 90;
    398     /* Enable background scan for configured networks */
    399     static final int CMD_ENABLE_BACKGROUND_SCAN           = BASE + 91;
    400     /* Enable TDLS on a specific MAC address */
    401     static final int CMD_ENABLE_TDLS                      = BASE + 92;
    402 
    403     /* Commands from/to the SupplicantStateTracker */
    404     /* Reset the supplicant state tracker */
    405     static final int CMD_RESET_SUPPLICANT_STATE           = BASE + 111;
    406 
    407     /* P2p commands */
    408     /* We are ok with no response here since we wont do much with it anyway */
    409     public static final int CMD_ENABLE_P2P                = BASE + 131;
    410     /* In order to shut down supplicant cleanly, we wait till p2p has
    411      * been disabled */
    412     public static final int CMD_DISABLE_P2P_REQ           = BASE + 132;
    413     public static final int CMD_DISABLE_P2P_RSP           = BASE + 133;
    414 
    415     public static final int CMD_BOOT_COMPLETED            = BASE + 134;
    416 
    417     /* change the batch scan settings.
    418      * arg1 = responsible UID
    419      * obj = the new settings
    420      */
    421     public static final int CMD_SET_BATCHED_SCAN          = BASE + 135;
    422     public static final int CMD_START_NEXT_BATCHED_SCAN   = BASE + 136;
    423     public static final int CMD_POLL_BATCHED_SCAN         = BASE + 137;
    424 
    425     /* Link configuration (IP address, DNS, ...) changes */
    426     /* An new IP address was added to our interface, or an existing IP address was updated */
    427     static final int CMD_IP_ADDRESS_UPDATED               = BASE + 140;
    428     /* An IP address was removed from our interface */
    429     static final int CMD_IP_ADDRESS_REMOVED               = BASE + 141;
    430     /* Reload all networks and reconnect */
    431     static final int CMD_RELOAD_TLS_AND_RECONNECT         = BASE + 142;
    432 
    433     /* Wifi state machine modes of operation */
    434     /* CONNECT_MODE - connect to any 'known' AP when it becomes available */
    435     public static final int CONNECT_MODE                   = 1;
    436     /* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */
    437     public static final int SCAN_ONLY_MODE                 = 2;
    438     /* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */
    439     public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE   = 3;
    440 
    441     private static final int SUCCESS = 1;
    442     private static final int FAILURE = -1;
    443 
    444     /**
    445      * The maximum number of times we will retry a connection to an access point
    446      * for which we have failed in acquiring an IP address from DHCP. A value of
    447      * N means that we will make N+1 connection attempts in all.
    448      * <p>
    449      * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
    450      * value if a Settings value is not present.
    451      */
    452     private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
    453 
    454     /* Tracks if suspend optimizations need to be disabled by DHCP,
    455      * screen or due to high perf mode.
    456      * When any of them needs to disable it, we keep the suspend optimizations
    457      * disabled
    458      */
    459     private int mSuspendOptNeedsDisabled = 0;
    460 
    461     private static final int SUSPEND_DUE_TO_DHCP       = 1;
    462     private static final int SUSPEND_DUE_TO_HIGH_PERF  = 1<<1;
    463     private static final int SUSPEND_DUE_TO_SCREEN     = 1<<2;
    464 
    465     /* Tracks if user has enabled suspend optimizations through settings */
    466     private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
    467 
    468     /**
    469      * Default framework scan interval in milliseconds. This is used in the scenario in which
    470      * wifi chipset does not support background scanning to set up a
    471      * periodic wake up scan so that the device can connect to a new access
    472      * point on the move. {@link Settings.Global#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can
    473      * override this.
    474      */
    475     private final int mDefaultFrameworkScanIntervalMs;
    476 
    477     /**
    478      * Supplicant scan interval in milliseconds.
    479      * Comes from {@link Settings.Global#WIFI_SUPPLICANT_SCAN_INTERVAL_MS} or
    480      * from the default config if the setting is not set
    481      */
    482     private long mSupplicantScanIntervalMs;
    483 
    484     /**
    485      * Minimum time interval between enabling all networks.
    486      * A device can end up repeatedly connecting to a bad network on screen on/off toggle
    487      * due to enabling every time. We add a threshold to avoid this.
    488      */
    489     private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
    490     private long mLastEnableAllNetworksTime;
    491 
    492     /**
    493      * Starting and shutting down driver too quick causes problems leading to driver
    494      * being in a bad state. Delay driver stop.
    495      */
    496     private final int mDriverStopDelayMs;
    497     private int mDelayedStopCounter;
    498     private boolean mInDelayedStop = false;
    499 
    500     // sometimes telephony gives us this data before boot is complete and we can't store it
    501     // until after, so the write is deferred
    502     private volatile String mPersistedCountryCode;
    503 
    504     // Supplicant doesn't like setting the same country code multiple times (it may drop
    505     // currently connected network), so we save the country code here to avoid redundency
    506     private String mLastSetCountryCode;
    507 
    508     private static final int MIN_RSSI = -200;
    509     private static final int MAX_RSSI = 256;
    510 
    511     /* Default parent state */
    512     private State mDefaultState = new DefaultState();
    513     /* Temporary initial state */
    514     private State mInitialState = new InitialState();
    515     /* Driver loaded, waiting for supplicant to start */
    516     private State mSupplicantStartingState = new SupplicantStartingState();
    517     /* Driver loaded and supplicant ready */
    518     private State mSupplicantStartedState = new SupplicantStartedState();
    519     /* Waiting for supplicant to stop and monitor to exit */
    520     private State mSupplicantStoppingState = new SupplicantStoppingState();
    521     /* Driver start issued, waiting for completed event */
    522     private State mDriverStartingState = new DriverStartingState();
    523     /* Driver started */
    524     private State mDriverStartedState = new DriverStartedState();
    525     /* Wait until p2p is disabled
    526      * This is a special state which is entered right after we exit out of DriverStartedState
    527      * before transitioning to another state.
    528      */
    529     private State mWaitForP2pDisableState = new WaitForP2pDisableState();
    530     /* Driver stopping */
    531     private State mDriverStoppingState = new DriverStoppingState();
    532     /* Driver stopped */
    533     private State mDriverStoppedState = new DriverStoppedState();
    534     /* Scan for networks, no connection will be established */
    535     private State mScanModeState = new ScanModeState();
    536     /* Connecting to an access point */
    537     private State mConnectModeState = new ConnectModeState();
    538     /* Connected at 802.11 (L2) level */
    539     private State mL2ConnectedState = new L2ConnectedState();
    540     /* fetching IP after connection to access point (assoc+auth complete) */
    541     private State mObtainingIpState = new ObtainingIpState();
    542     /* Waiting for link quality verification to be complete */
    543     private State mVerifyingLinkState = new VerifyingLinkState();
    544     /* Waiting for captive portal check to be complete */
    545     private State mCaptivePortalCheckState = new CaptivePortalCheckState();
    546     /* Connected with IP addr */
    547     private State mConnectedState = new ConnectedState();
    548     /* disconnect issued, waiting for network disconnect confirmation */
    549     private State mDisconnectingState = new DisconnectingState();
    550     /* Network is not connected, supplicant assoc+auth is not complete */
    551     private State mDisconnectedState = new DisconnectedState();
    552     /* Waiting for WPS to be completed*/
    553     private State mWpsRunningState = new WpsRunningState();
    554 
    555     /* Soft ap is starting up */
    556     private State mSoftApStartingState = new SoftApStartingState();
    557     /* Soft ap is running */
    558     private State mSoftApStartedState = new SoftApStartedState();
    559     /* Soft ap is running and we are waiting for tether notification */
    560     private State mTetheringState = new TetheringState();
    561     /* Soft ap is running and we are tethered through connectivity service */
    562     private State mTetheredState = new TetheredState();
    563     /* Waiting for untether confirmation before stopping soft Ap */
    564     private State mUntetheringState = new UntetheringState();
    565 
    566     private class TetherStateChange {
    567         ArrayList<String> available;
    568         ArrayList<String> active;
    569         TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
    570             available = av;
    571             active = ac;
    572         }
    573     }
    574 
    575 
    576     /**
    577      * One of  {@link WifiManager#WIFI_STATE_DISABLED},
    578      *         {@link WifiManager#WIFI_STATE_DISABLING},
    579      *         {@link WifiManager#WIFI_STATE_ENABLED},
    580      *         {@link WifiManager#WIFI_STATE_ENABLING},
    581      *         {@link WifiManager#WIFI_STATE_UNKNOWN}
    582      *
    583      */
    584     private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
    585 
    586     /**
    587      * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
    588      *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
    589      *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
    590      *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
    591      *         {@link WifiManager#WIFI_AP_STATE_FAILED}
    592      *
    593      */
    594     private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
    595 
    596     private static final int SCAN_REQUEST = 0;
    597     private static final String ACTION_START_SCAN =
    598         "com.android.server.WifiManager.action.START_SCAN";
    599 
    600     private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter";
    601     private static final int DRIVER_STOP_REQUEST = 0;
    602     private static final String ACTION_DELAYED_DRIVER_STOP =
    603         "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
    604 
    605     private static final String ACTION_REFRESH_BATCHED_SCAN =
    606             "com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN";
    607     /**
    608      * Keep track of whether WIFI is running.
    609      */
    610     private boolean mIsRunning = false;
    611 
    612     /**
    613      * Keep track of whether we last told the battery stats we had started.
    614      */
    615     private boolean mReportedRunning = false;
    616 
    617     /**
    618      * Most recently set source of starting WIFI.
    619      */
    620     private final WorkSource mRunningWifiUids = new WorkSource();
    621 
    622     /**
    623      * The last reported UIDs that were responsible for starting WIFI.
    624      */
    625     private final WorkSource mLastRunningWifiUids = new WorkSource();
    626 
    627     private final IBatteryStats mBatteryStats;
    628 
    629     private BatchedScanSettings mBatchedScanSettings = null;
    630 
    631 
    632     public WifiStateMachine(Context context, String wlanInterface) {
    633         super("WifiStateMachine");
    634         mContext = context;
    635         mInterfaceName = wlanInterface;
    636 
    637         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
    638         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
    639                 BatteryStats.SERVICE_NAME));
    640 
    641         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
    642         mNwService = INetworkManagementService.Stub.asInterface(b);
    643 
    644         mP2pSupported = mContext.getPackageManager().hasSystemFeature(
    645                 PackageManager.FEATURE_WIFI_DIRECT);
    646 
    647         mWifiNative = new WifiNative(mInterfaceName);
    648         mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
    649         mWifiMonitor = new WifiMonitor(this, mWifiNative);
    650         mWifiInfo = new WifiInfo();
    651         mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
    652                 getHandler());
    653         mLinkProperties = new LinkProperties();
    654         mNetlinkLinkProperties = new LinkProperties();
    655 
    656         mWifiP2pManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE);
    657 
    658         mNetworkInfo.setIsAvailable(false);
    659         mLastBssid = null;
    660         mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
    661         mLastSignalLevel = -1;
    662 
    663         mInterfaceObserver = new InterfaceObserver(this);
    664         try {
    665             mNwService.registerObserver(mInterfaceObserver);
    666         } catch (RemoteException e) {
    667             loge("Couldn't register interface observer: " + e.toString());
    668         }
    669 
    670         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
    671         Intent scanIntent = new Intent(ACTION_START_SCAN, null);
    672         mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
    673 
    674         Intent batchedIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
    675         mBatchedScanIntervalIntent = PendingIntent.getBroadcast(mContext, 0, batchedIntent, 0);
    676 
    677         mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger(
    678                 R.integer.config_wifi_framework_scan_interval);
    679 
    680         mDriverStopDelayMs = mContext.getResources().getInteger(
    681                 R.integer.config_wifi_driver_stop_delay);
    682 
    683         mBackgroundScanSupported = mContext.getResources().getBoolean(
    684                 R.bool.config_wifi_background_scan_support);
    685 
    686         mPrimaryDeviceType = mContext.getResources().getString(
    687                 R.string.config_wifi_p2p_device_type);
    688 
    689         mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
    690                     Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
    691 
    692         mContext.registerReceiver(
    693             new BroadcastReceiver() {
    694                 @Override
    695                 public void onReceive(Context context, Intent intent) {
    696                     ArrayList<String> available = intent.getStringArrayListExtra(
    697                             ConnectivityManager.EXTRA_AVAILABLE_TETHER);
    698                     ArrayList<String> active = intent.getStringArrayListExtra(
    699                             ConnectivityManager.EXTRA_ACTIVE_TETHER);
    700                     sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
    701                 }
    702             },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
    703 
    704         mContext.registerReceiver(
    705                 new BroadcastReceiver() {
    706                     @Override
    707                     public void onReceive(Context context, Intent intent) {
    708                         final WorkSource workSource = null;
    709                         startScan(UNKNOWN_SCAN_SOURCE, workSource);
    710                     }
    711                 },
    712                 new IntentFilter(ACTION_START_SCAN));
    713 
    714         IntentFilter filter = new IntentFilter();
    715         filter.addAction(Intent.ACTION_SCREEN_ON);
    716         filter.addAction(Intent.ACTION_SCREEN_OFF);
    717         filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
    718         mContext.registerReceiver(
    719                 new BroadcastReceiver() {
    720                     @Override
    721                     public void onReceive(Context context, Intent intent) {
    722                         String action = intent.getAction();
    723 
    724                         if (action.equals(Intent.ACTION_SCREEN_ON)) {
    725                             handleScreenStateChanged(true);
    726                         } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
    727                             handleScreenStateChanged(false);
    728                         } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
    729                             startNextBatchedScanAsync();
    730                         }
    731                     }
    732                 }, filter);
    733 
    734         mContext.registerReceiver(
    735                 new BroadcastReceiver() {
    736                     @Override
    737                     public void onReceive(Context context, Intent intent) {
    738                        int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
    739                        sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
    740                     }
    741                 },
    742                 new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
    743 
    744         mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
    745                 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
    746                 new ContentObserver(getHandler()) {
    747                     @Override
    748                     public void onChange(boolean selfChange) {
    749                         mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
    750                                 Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
    751                     }
    752                 });
    753 
    754         mContext.registerReceiver(
    755                 new BroadcastReceiver() {
    756                     @Override
    757                     public void onReceive(Context context, Intent intent) {
    758                         sendMessage(CMD_BOOT_COMPLETED);
    759                     }
    760                 },
    761                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
    762 
    763         mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE);
    764 
    765         PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
    766         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName());
    767 
    768         mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
    769         mSuspendWakeLock.setReferenceCounted(false);
    770 
    771         addState(mDefaultState);
    772             addState(mInitialState, mDefaultState);
    773             addState(mSupplicantStartingState, mDefaultState);
    774             addState(mSupplicantStartedState, mDefaultState);
    775                 addState(mDriverStartingState, mSupplicantStartedState);
    776                 addState(mDriverStartedState, mSupplicantStartedState);
    777                     addState(mScanModeState, mDriverStartedState);
    778                     addState(mConnectModeState, mDriverStartedState);
    779                         addState(mL2ConnectedState, mConnectModeState);
    780                             addState(mObtainingIpState, mL2ConnectedState);
    781                             addState(mVerifyingLinkState, mL2ConnectedState);
    782                             addState(mCaptivePortalCheckState, mL2ConnectedState);
    783                             addState(mConnectedState, mL2ConnectedState);
    784                         addState(mDisconnectingState, mConnectModeState);
    785                         addState(mDisconnectedState, mConnectModeState);
    786                         addState(mWpsRunningState, mConnectModeState);
    787                 addState(mWaitForP2pDisableState, mSupplicantStartedState);
    788                 addState(mDriverStoppingState, mSupplicantStartedState);
    789                 addState(mDriverStoppedState, mSupplicantStartedState);
    790             addState(mSupplicantStoppingState, mDefaultState);
    791             addState(mSoftApStartingState, mDefaultState);
    792             addState(mSoftApStartedState, mDefaultState);
    793                 addState(mTetheringState, mSoftApStartedState);
    794                 addState(mTetheredState, mSoftApStartedState);
    795                 addState(mUntetheringState, mSoftApStartedState);
    796 
    797         setInitialState(mInitialState);
    798 
    799         setLogRecSize(2000);
    800         setLogOnlyTransitions(false);
    801         if (DBG) setDbg(true);
    802 
    803         //start the state machine
    804         start();
    805 
    806         final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
    807         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    808         intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
    809         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    810     }
    811 
    812     /*********************************************************
    813      * Methods exposed for public use
    814      ********************************************************/
    815 
    816     public Messenger getMessenger() {
    817         return new Messenger(getHandler());
    818     }
    819     /**
    820      * TODO: doc
    821      */
    822     public boolean syncPingSupplicant(AsyncChannel channel) {
    823         Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
    824         boolean result = (resultMsg.arg1 != FAILURE);
    825         resultMsg.recycle();
    826         return result;
    827     }
    828 
    829     /**
    830      * Initiate a wifi scan.  If workSource is not null, blame is given to it,
    831      * otherwise blame is given to callingUid.
    832      *
    833      * @param callingUid The uid initiating the wifi scan.  Blame will be given
    834      *                   here unless workSource is specified.
    835      * @param workSource If not null, blame is given to workSource.
    836      */
    837     public void startScan(int callingUid, WorkSource workSource) {
    838         sendMessage(CMD_START_SCAN, callingUid, 0, workSource);
    839     }
    840 
    841     /**
    842      * start or stop batched scanning using the given settings
    843      */
    844     public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid) {
    845         sendMessage(CMD_SET_BATCHED_SCAN, callingUid, 0, settings);
    846     }
    847 
    848     public List<BatchedScanResult> syncGetBatchedScanResultsList() {
    849         synchronized (mBatchedScanResults) {
    850             List<BatchedScanResult> batchedScanList =
    851                     new ArrayList<BatchedScanResult>(mBatchedScanResults.size());
    852             for(BatchedScanResult result: mBatchedScanResults) {
    853                 batchedScanList.add(new BatchedScanResult(result));
    854             }
    855             return batchedScanList;
    856         }
    857     }
    858 
    859     public void requestBatchedScanPoll() {
    860         sendMessage(CMD_POLL_BATCHED_SCAN);
    861     }
    862 
    863     private void startBatchedScan() {
    864         if (mDhcpActive) {
    865             if (DBG) log("not starting Batched Scans due to DHCP");
    866             return;
    867         }
    868 
    869         // first grab any existing data
    870         retrieveBatchedScanData();
    871 
    872         mAlarmManager.cancel(mBatchedScanIntervalIntent);
    873 
    874         String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings);
    875 
    876         try {
    877             mExpectedBatchedScans = Integer.parseInt(scansExpected);
    878             setNextBatchedAlarm(mExpectedBatchedScans);
    879         } catch (NumberFormatException e) {
    880             stopBatchedScan();
    881             loge("Exception parsing WifiNative.setBatchedScanSettings response " + e);
    882         }
    883     }
    884 
    885     // called from BroadcastListener
    886     private void startNextBatchedScanAsync() {
    887         sendMessage(CMD_START_NEXT_BATCHED_SCAN);
    888     }
    889 
    890     private void startNextBatchedScan() {
    891         // first grab any existing data
    892         retrieveBatchedScanData();
    893 
    894         setNextBatchedAlarm(mExpectedBatchedScans);
    895     }
    896 
    897     private void handleBatchedScanPollRequest() {
    898         if (DBG) {
    899             log("handleBatchedScanPoll Request - mBatchedScanMinPollTime=" +
    900                     mBatchedScanMinPollTime + " , mBatchedScanSettings=" +
    901                     mBatchedScanSettings);
    902         }
    903         // if there is no appropriate PollTime that's because we either aren't
    904         // batching or we've already set a time for a poll request
    905         if (mBatchedScanMinPollTime == 0) return;
    906         if (mBatchedScanSettings == null) return;
    907 
    908         long now = System.currentTimeMillis();
    909 
    910         if (now > mBatchedScanMinPollTime) {
    911             // do the poll and reset our timers
    912             startNextBatchedScan();
    913         } else {
    914             mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, mBatchedScanMinPollTime,
    915                     mBatchedScanIntervalIntent);
    916             mBatchedScanMinPollTime = 0;
    917         }
    918     }
    919 
    920     // return true if new/different
    921     private boolean recordBatchedScanSettings(BatchedScanSettings settings) {
    922         if (DBG) log("set batched scan to " + settings);
    923         if (settings != null) {
    924             // TODO - noteBatchedScanStart(message.arg1);
    925             if (settings.equals(mBatchedScanSettings)) return false;
    926         } else {
    927             if (mBatchedScanSettings == null) return false;
    928             // TODO - noteBatchedScanStop(message.arg1);
    929         }
    930         mBatchedScanSettings = settings;
    931         return true;
    932     }
    933 
    934     private void stopBatchedScan() {
    935         mAlarmManager.cancel(mBatchedScanIntervalIntent);
    936         if (mBatchedScanSettings != null) {
    937             retrieveBatchedScanData();
    938             mWifiNative.setBatchedScanSettings(null);
    939         }
    940     }
    941 
    942     private void setNextBatchedAlarm(int scansExpected) {
    943 
    944         if (mBatchedScanSettings == null || scansExpected < 1) return;
    945 
    946         mBatchedScanMinPollTime = System.currentTimeMillis() +
    947                 mBatchedScanSettings.scanIntervalSec * 1000;
    948 
    949         if (mBatchedScanSettings.maxScansPerBatch < scansExpected) {
    950             scansExpected = mBatchedScanSettings.maxScansPerBatch;
    951         }
    952 
    953         int secToFull = mBatchedScanSettings.scanIntervalSec;
    954         secToFull *= scansExpected;
    955 
    956         int debugPeriod = SystemProperties.getInt("wifi.batchedScan.pollPeriod", 0);
    957         if (debugPeriod > 0) secToFull = debugPeriod;
    958 
    959         // set the alarm to do the next poll.  We set it a little short as we'd rather
    960         // wake up wearly than miss a scan due to buffer overflow
    961         mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
    962                 + ((secToFull - (mBatchedScanSettings.scanIntervalSec / 2)) * 1000),
    963                 mBatchedScanIntervalIntent);
    964     }
    965 
    966     /**
    967      * Start reading new scan data
    968      * Data comes in as:
    969      * "scancount=5\n"
    970      * "nextcount=5\n"
    971      *   "apcount=3\n"
    972      *   "trunc\n" (optional)
    973      *     "bssid=...\n"
    974      *     "ssid=...\n"
    975      *     "freq=...\n" (in Mhz)
    976      *     "level=...\n"
    977      *     "dist=...\n" (in cm)
    978      *     "distsd=...\n" (standard deviation, in cm)
    979      *     "===="
    980      *     "bssid=...\n"
    981      *     etc
    982      *     "===="
    983      *     "bssid=...\n"
    984      *     etc
    985      *     "%%%%"
    986      *   "apcount=2\n"
    987      *     "bssid=...\n"
    988      *     etc
    989      *     "%%%%
    990      *   etc
    991      *   "----"
    992      */
    993     private final static boolean DEBUG_PARSE = false;
    994     private void retrieveBatchedScanData() {
    995         String rawData = mWifiNative.getBatchedScanResults();
    996         if (DEBUG_PARSE) log("rawData = " + rawData);
    997         mBatchedScanMinPollTime = 0;
    998         if (rawData == null || rawData.equalsIgnoreCase("OK")) {
    999             loge("Unexpected BatchedScanResults :" + rawData);
   1000             return;
   1001         }
   1002 
   1003         int scanCount = 0;
   1004         final String END_OF_BATCHES = "----";
   1005         final String SCANCOUNT = "scancount=";
   1006         final String TRUNCATED = "trunc";
   1007         final String AGE = "age=";
   1008         final String DIST = "dist=";
   1009         final String DISTSD = "distSd=";
   1010 
   1011         String splitData[] = rawData.split("\n");
   1012         int n = 0;
   1013         if (splitData[n].startsWith(SCANCOUNT)) {
   1014             try {
   1015                 scanCount = Integer.parseInt(splitData[n++].substring(SCANCOUNT.length()));
   1016             } catch (NumberFormatException e) {
   1017                 loge("scancount parseInt Exception from " + splitData[n]);
   1018             }
   1019         } else log("scancount not found");
   1020         if (scanCount == 0) {
   1021             loge("scanCount==0 - aborting");
   1022             return;
   1023         }
   1024 
   1025         final Intent intent = new Intent(WifiManager.BATCHED_SCAN_RESULTS_AVAILABLE_ACTION);
   1026         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1027 
   1028         synchronized (mBatchedScanResults) {
   1029             mBatchedScanResults.clear();
   1030             BatchedScanResult batchedScanResult = new BatchedScanResult();
   1031 
   1032             String bssid = null;
   1033             WifiSsid wifiSsid = null;
   1034             int level = 0;
   1035             int freq = 0;
   1036             int dist, distSd;
   1037             long tsf = 0;
   1038             dist = distSd = ScanResult.UNSPECIFIED;
   1039             final long now = SystemClock.elapsedRealtime();
   1040             final int bssidStrLen = BSSID_STR.length();
   1041 
   1042             while (true) {
   1043                 while (n < splitData.length) {
   1044                     if (DEBUG_PARSE) logd("parsing " + splitData[n]);
   1045                     if (splitData[n].equals(END_OF_BATCHES)) {
   1046                         if (n+1 != splitData.length) {
   1047                             loge("didn't consume " + (splitData.length-n));
   1048                         }
   1049                         if (mBatchedScanResults.size() > 0) {
   1050                             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
   1051                         }
   1052                         logd("retrieveBatchedScanResults X");
   1053                         return;
   1054                     }
   1055                     if ((splitData[n].equals(END_STR)) || splitData[n].equals(DELIMITER_STR)) {
   1056                         if (bssid != null) {
   1057                             batchedScanResult.scanResults.add(new ScanResult(
   1058                                     wifiSsid, bssid, "", level, freq, tsf, dist, distSd));
   1059                             wifiSsid = null;
   1060                             bssid = null;
   1061                             level = 0;
   1062                             freq = 0;
   1063                             tsf = 0;
   1064                             dist = distSd = ScanResult.UNSPECIFIED;
   1065                         }
   1066                         if (splitData[n].equals(END_STR)) {
   1067                             if (batchedScanResult.scanResults.size() != 0) {
   1068                                 mBatchedScanResults.add(batchedScanResult);
   1069                                 batchedScanResult = new BatchedScanResult();
   1070                             } else {
   1071                                 logd("Found empty batch");
   1072                             }
   1073                         }
   1074                     } else if (splitData[n].equals(TRUNCATED)) {
   1075                         batchedScanResult.truncated = true;
   1076                     } else if (splitData[n].startsWith(BSSID_STR)) {
   1077                         bssid = new String(splitData[n].getBytes(), bssidStrLen,
   1078                                 splitData[n].length() - bssidStrLen);
   1079                     } else if (splitData[n].startsWith(FREQ_STR)) {
   1080                         try {
   1081                             freq = Integer.parseInt(splitData[n].substring(FREQ_STR.length()));
   1082                         } catch (NumberFormatException e) {
   1083                             loge("Invalid freqency: " + splitData[n]);
   1084                             freq = 0;
   1085                         }
   1086                     } else if (splitData[n].startsWith(AGE)) {
   1087                         try {
   1088                             tsf = now - Long.parseLong(splitData[n].substring(AGE.length()));
   1089                             tsf *= 1000; // convert mS -> uS
   1090                         } catch (NumberFormatException e) {
   1091                             loge("Invalid timestamp: " + splitData[n]);
   1092                             tsf = 0;
   1093                         }
   1094                     } else if (splitData[n].startsWith(SSID_STR)) {
   1095                         wifiSsid = WifiSsid.createFromAsciiEncoded(
   1096                                 splitData[n].substring(SSID_STR.length()));
   1097                     } else if (splitData[n].startsWith(LEVEL_STR)) {
   1098                         try {
   1099                             level = Integer.parseInt(splitData[n].substring(LEVEL_STR.length()));
   1100                             if (level > 0) level -= 256;
   1101                         } catch (NumberFormatException e) {
   1102                             loge("Invalid level: " + splitData[n]);
   1103                             level = 0;
   1104                         }
   1105                     } else if (splitData[n].startsWith(DIST)) {
   1106                         try {
   1107                             dist = Integer.parseInt(splitData[n].substring(DIST.length()));
   1108                         } catch (NumberFormatException e) {
   1109                             loge("Invalid distance: " + splitData[n]);
   1110                             dist = ScanResult.UNSPECIFIED;
   1111                         }
   1112                     } else if (splitData[n].startsWith(DISTSD)) {
   1113                         try {
   1114                             distSd = Integer.parseInt(splitData[n].substring(DISTSD.length()));
   1115                         } catch (NumberFormatException e) {
   1116                             loge("Invalid distanceSd: " + splitData[n]);
   1117                             distSd = ScanResult.UNSPECIFIED;
   1118                         }
   1119                     } else {
   1120                         loge("Unable to parse batched scan result line: " + splitData[n]);
   1121                     }
   1122                     n++;
   1123                 }
   1124                 rawData = mWifiNative.getBatchedScanResults();
   1125                 if (DEBUG_PARSE) log("reading more data:\n" + rawData);
   1126                 if (rawData == null) {
   1127                     loge("Unexpected null BatchedScanResults");
   1128                     return;
   1129                 }
   1130                 splitData = rawData.split("\n");
   1131                 if (splitData.length == 0 || splitData[0].equals("ok")) {
   1132                     loge("batch scan results just ended!");
   1133                     if (mBatchedScanResults.size() > 0) {
   1134                         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1135                     }
   1136                     return;
   1137                 }
   1138                 n = 0;
   1139             }
   1140         }
   1141     }
   1142 
   1143     // If workSource is not null, blame is given to it, otherwise blame is given to callingUid.
   1144     private void noteScanStart(int callingUid, WorkSource workSource) {
   1145         if (mScanWorkSource == null && (callingUid != UNKNOWN_SCAN_SOURCE || workSource != null)) {
   1146             mScanWorkSource = workSource != null ? workSource : new WorkSource(callingUid);
   1147             try {
   1148                 mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
   1149             } catch (RemoteException e) {
   1150                 log(e.toString());
   1151             }
   1152         }
   1153     }
   1154 
   1155     private void noteScanEnd() {
   1156         if (mScanWorkSource != null) {
   1157             try {
   1158                 mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
   1159             } catch (RemoteException e) {
   1160                 log(e.toString());
   1161             } finally {
   1162                 mScanWorkSource = null;
   1163             }
   1164         }
   1165     }
   1166 
   1167     private void startScanNative(int type) {
   1168         mWifiNative.scan(type);
   1169         mScanResultIsPending = true;
   1170     }
   1171 
   1172     /**
   1173      * TODO: doc
   1174      */
   1175     public void setSupplicantRunning(boolean enable) {
   1176         if (enable) {
   1177             sendMessage(CMD_START_SUPPLICANT);
   1178         } else {
   1179             sendMessage(CMD_STOP_SUPPLICANT);
   1180         }
   1181     }
   1182 
   1183     /**
   1184      * TODO: doc
   1185      */
   1186     public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
   1187         if (enable) {
   1188             sendMessage(CMD_START_AP, wifiConfig);
   1189         } else {
   1190             sendMessage(CMD_STOP_AP);
   1191         }
   1192     }
   1193 
   1194     public void setWifiApConfiguration(WifiConfiguration config) {
   1195         mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
   1196     }
   1197 
   1198     public WifiConfiguration syncGetWifiApConfiguration() {
   1199         Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG);
   1200         WifiConfiguration ret = (WifiConfiguration) resultMsg.obj;
   1201         resultMsg.recycle();
   1202         return ret;
   1203     }
   1204 
   1205     /**
   1206      * TODO: doc
   1207      */
   1208     public int syncGetWifiState() {
   1209         return mWifiState.get();
   1210     }
   1211 
   1212     /**
   1213      * TODO: doc
   1214      */
   1215     public String syncGetWifiStateByName() {
   1216         switch (mWifiState.get()) {
   1217             case WIFI_STATE_DISABLING:
   1218                 return "disabling";
   1219             case WIFI_STATE_DISABLED:
   1220                 return "disabled";
   1221             case WIFI_STATE_ENABLING:
   1222                 return "enabling";
   1223             case WIFI_STATE_ENABLED:
   1224                 return "enabled";
   1225             case WIFI_STATE_UNKNOWN:
   1226                 return "unknown state";
   1227             default:
   1228                 return "[invalid state]";
   1229         }
   1230     }
   1231 
   1232     /**
   1233      * TODO: doc
   1234      */
   1235     public int syncGetWifiApState() {
   1236         return mWifiApState.get();
   1237     }
   1238 
   1239     /**
   1240      * TODO: doc
   1241      */
   1242     public String syncGetWifiApStateByName() {
   1243         switch (mWifiApState.get()) {
   1244             case WIFI_AP_STATE_DISABLING:
   1245                 return "disabling";
   1246             case WIFI_AP_STATE_DISABLED:
   1247                 return "disabled";
   1248             case WIFI_AP_STATE_ENABLING:
   1249                 return "enabling";
   1250             case WIFI_AP_STATE_ENABLED:
   1251                 return "enabled";
   1252             case WIFI_AP_STATE_FAILED:
   1253                 return "failed";
   1254             default:
   1255                 return "[invalid state]";
   1256         }
   1257     }
   1258 
   1259     /**
   1260      * Get status information for the current connection, if any.
   1261      * @return a {@link WifiInfo} object containing information about the current connection
   1262      *
   1263      */
   1264     public WifiInfo syncRequestConnectionInfo() {
   1265         return mWifiInfo;
   1266     }
   1267 
   1268     public DhcpResults syncGetDhcpResults() {
   1269         synchronized (mDhcpResultsLock) {
   1270             return new DhcpResults(mDhcpResults);
   1271         }
   1272     }
   1273 
   1274     /**
   1275      * TODO: doc
   1276      */
   1277     public void setDriverStart(boolean enable) {
   1278         if (enable) {
   1279             sendMessage(CMD_START_DRIVER);
   1280         } else {
   1281             sendMessage(CMD_STOP_DRIVER);
   1282         }
   1283     }
   1284 
   1285     public void captivePortalCheckComplete() {
   1286         sendMessage(CMD_CAPTIVE_CHECK_COMPLETE);
   1287     }
   1288 
   1289     /**
   1290      * TODO: doc
   1291      */
   1292     public void setOperationalMode(int mode) {
   1293         if (DBG) log("setting operational mode to " + String.valueOf(mode));
   1294         sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
   1295     }
   1296 
   1297     /**
   1298      * TODO: doc
   1299      */
   1300     public List<ScanResult> syncGetScanResultsList() {
   1301         synchronized (mScanResultCache) {
   1302             List<ScanResult> scanList = new ArrayList<ScanResult>();
   1303             for(ScanResult result: mScanResults) {
   1304                 scanList.add(new ScanResult(result));
   1305             }
   1306             return scanList;
   1307         }
   1308     }
   1309 
   1310     /**
   1311      * Disconnect from Access Point
   1312      */
   1313     public void disconnectCommand() {
   1314         sendMessage(CMD_DISCONNECT);
   1315     }
   1316 
   1317     /**
   1318      * Initiate a reconnection to AP
   1319      */
   1320     public void reconnectCommand() {
   1321         sendMessage(CMD_RECONNECT);
   1322     }
   1323 
   1324     /**
   1325      * Initiate a re-association to AP
   1326      */
   1327     public void reassociateCommand() {
   1328         sendMessage(CMD_REASSOCIATE);
   1329     }
   1330 
   1331     /**
   1332      * Reload networks and then reconnect; helps load correct data for TLS networks
   1333      */
   1334 
   1335     public void reloadTlsNetworksAndReconnect() {
   1336         sendMessage(CMD_RELOAD_TLS_AND_RECONNECT);
   1337     }
   1338 
   1339     /**
   1340      * Add a network synchronously
   1341      *
   1342      * @return network id of the new network
   1343      */
   1344     public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
   1345         Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
   1346         int result = resultMsg.arg1;
   1347         resultMsg.recycle();
   1348         return result;
   1349     }
   1350 
   1351     public List<WifiConfiguration> syncGetConfiguredNetworks(AsyncChannel channel) {
   1352         Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS);
   1353         List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
   1354         resultMsg.recycle();
   1355         return result;
   1356     }
   1357 
   1358     /**
   1359      * Delete a network
   1360      *
   1361      * @param networkId id of the network to be removed
   1362      */
   1363     public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
   1364         Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
   1365         boolean result = (resultMsg.arg1 != FAILURE);
   1366         resultMsg.recycle();
   1367         return result;
   1368     }
   1369 
   1370     /**
   1371      * Enable a network
   1372      *
   1373      * @param netId network id of the network
   1374      * @param disableOthers true, if all other networks have to be disabled
   1375      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1376      */
   1377     public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
   1378         Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
   1379                 disableOthers ? 1 : 0);
   1380         boolean result = (resultMsg.arg1 != FAILURE);
   1381         resultMsg.recycle();
   1382         return result;
   1383     }
   1384 
   1385     /**
   1386      * Disable a network
   1387      *
   1388      * @param netId network id of the network
   1389      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1390      */
   1391     public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
   1392         Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
   1393         boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
   1394         resultMsg.recycle();
   1395         return result;
   1396     }
   1397 
   1398     /**
   1399      * Blacklist a BSSID. This will avoid the AP if there are
   1400      * alternate APs to connect
   1401      *
   1402      * @param bssid BSSID of the network
   1403      */
   1404     public void addToBlacklist(String bssid) {
   1405         sendMessage(CMD_BLACKLIST_NETWORK, bssid);
   1406     }
   1407 
   1408     /**
   1409      * Clear the blacklist list
   1410      *
   1411      */
   1412     public void clearBlacklist() {
   1413         sendMessage(CMD_CLEAR_BLACKLIST);
   1414     }
   1415 
   1416     public void enableRssiPolling(boolean enabled) {
   1417        sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
   1418     }
   1419 
   1420     public void enableBackgroundScanCommand(boolean enabled) {
   1421        sendMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0);
   1422     }
   1423 
   1424     public void enableAllNetworks() {
   1425         sendMessage(CMD_ENABLE_ALL_NETWORKS);
   1426     }
   1427 
   1428     /**
   1429      * Start filtering Multicast v4 packets
   1430      */
   1431     public void startFilteringMulticastV4Packets() {
   1432         mFilteringMulticastV4Packets.set(true);
   1433         sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0);
   1434     }
   1435 
   1436     /**
   1437      * Stop filtering Multicast v4 packets
   1438      */
   1439     public void stopFilteringMulticastV4Packets() {
   1440         mFilteringMulticastV4Packets.set(false);
   1441         sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0);
   1442     }
   1443 
   1444     /**
   1445      * Start filtering Multicast v4 packets
   1446      */
   1447     public void startFilteringMulticastV6Packets() {
   1448         sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0);
   1449     }
   1450 
   1451     /**
   1452      * Stop filtering Multicast v4 packets
   1453      */
   1454     public void stopFilteringMulticastV6Packets() {
   1455         sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0);
   1456     }
   1457 
   1458     /**
   1459      * Set high performance mode of operation.
   1460      * Enabling would set active power mode and disable suspend optimizations;
   1461      * disabling would set auto power mode and enable suspend optimizations
   1462      * @param enable true if enable, false otherwise
   1463      */
   1464     public void setHighPerfModeEnabled(boolean enable) {
   1465         sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0);
   1466     }
   1467 
   1468     /**
   1469      * Set the country code
   1470      * @param countryCode following ISO 3166 format
   1471      * @param persist {@code true} if the setting should be remembered.
   1472      */
   1473     public void setCountryCode(String countryCode, boolean persist) {
   1474         if (persist) {
   1475             mPersistedCountryCode = countryCode;
   1476             Settings.Global.putString(mContext.getContentResolver(),
   1477                     Settings.Global.WIFI_COUNTRY_CODE,
   1478                     countryCode);
   1479         }
   1480         sendMessage(CMD_SET_COUNTRY_CODE, countryCode);
   1481         mWifiP2pChannel.sendMessage(WifiP2pService.SET_COUNTRY_CODE, countryCode);
   1482     }
   1483 
   1484     /**
   1485      * Set the operational frequency band
   1486      * @param band
   1487      * @param persist {@code true} if the setting should be remembered.
   1488      */
   1489     public void setFrequencyBand(int band, boolean persist) {
   1490         if (persist) {
   1491             Settings.Global.putInt(mContext.getContentResolver(),
   1492                     Settings.Global.WIFI_FREQUENCY_BAND,
   1493                     band);
   1494         }
   1495         sendMessage(CMD_SET_FREQUENCY_BAND, band, 0);
   1496     }
   1497 
   1498     /**
   1499      * Enable TDLS for a specific MAC address
   1500      */
   1501     public void enableTdls(String remoteMacAddress, boolean enable) {
   1502         int enabler = enable ? 1 : 0;
   1503         sendMessage(CMD_ENABLE_TDLS, enabler, 0, remoteMacAddress);
   1504     }
   1505 
   1506     /**
   1507      * Returns the operational frequency band
   1508      */
   1509     public int getFrequencyBand() {
   1510         return mFrequencyBand.get();
   1511     }
   1512 
   1513     /**
   1514      * Returns the wifi configuration file
   1515      */
   1516     public String getConfigFile() {
   1517         return mWifiConfigStore.getConfigFile();
   1518     }
   1519 
   1520     /**
   1521      * Send a message indicating bluetooth adapter connection state changed
   1522      */
   1523     public void sendBluetoothAdapterStateChange(int state) {
   1524         sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0);
   1525     }
   1526 
   1527     /**
   1528      * Save configuration on supplicant
   1529      *
   1530      * @return {@code true} if the operation succeeds, {@code false} otherwise
   1531      *
   1532      * TODO: deprecate this
   1533      */
   1534     public boolean syncSaveConfig(AsyncChannel channel) {
   1535         Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
   1536         boolean result = (resultMsg.arg1 != FAILURE);
   1537         resultMsg.recycle();
   1538         return result;
   1539     }
   1540 
   1541     public void updateBatteryWorkSource(WorkSource newSource) {
   1542         synchronized (mRunningWifiUids) {
   1543             try {
   1544                 if (newSource != null) {
   1545                     mRunningWifiUids.set(newSource);
   1546                 }
   1547                 if (mIsRunning) {
   1548                     if (mReportedRunning) {
   1549                         // If the work source has changed since last time, need
   1550                         // to remove old work from battery stats.
   1551                         if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
   1552                             mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
   1553                                     mRunningWifiUids);
   1554                             mLastRunningWifiUids.set(mRunningWifiUids);
   1555                         }
   1556                     } else {
   1557                         // Now being started, report it.
   1558                         mBatteryStats.noteWifiRunning(mRunningWifiUids);
   1559                         mLastRunningWifiUids.set(mRunningWifiUids);
   1560                         mReportedRunning = true;
   1561                     }
   1562                 } else {
   1563                     if (mReportedRunning) {
   1564                         // Last reported we were running, time to stop.
   1565                         mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
   1566                         mLastRunningWifiUids.clear();
   1567                         mReportedRunning = false;
   1568                     }
   1569                 }
   1570                 mWakeLock.setWorkSource(newSource);
   1571             } catch (RemoteException ignore) {
   1572             }
   1573         }
   1574     }
   1575 
   1576     @Override
   1577     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1578         super.dump(fd, pw, args);
   1579         mSupplicantStateTracker.dump(fd, pw, args);
   1580         pw.println("mLinkProperties " + mLinkProperties);
   1581         pw.println("mWifiInfo " + mWifiInfo);
   1582         pw.println("mDhcpResults " + mDhcpResults);
   1583         pw.println("mNetworkInfo " + mNetworkInfo);
   1584         pw.println("mLastSignalLevel " + mLastSignalLevel);
   1585         pw.println("mLastBssid " + mLastBssid);
   1586         pw.println("mLastNetworkId " + mLastNetworkId);
   1587         pw.println("mReconnectCount " + mReconnectCount);
   1588         pw.println("mOperationalMode " + mOperationalMode);
   1589         pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
   1590         pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
   1591         pw.println("Supplicant status " + mWifiNative.status());
   1592         pw.println("mEnableBackgroundScan " + mEnableBackgroundScan);
   1593         pw.println();
   1594         mWifiConfigStore.dump(fd, pw, args);
   1595     }
   1596 
   1597     /*********************************************************
   1598      * Internal private functions
   1599      ********************************************************/
   1600 
   1601     private void handleScreenStateChanged(boolean screenOn) {
   1602         if (DBG) log("handleScreenStateChanged: " + screenOn);
   1603         enableRssiPolling(screenOn);
   1604         if (mBackgroundScanSupported) {
   1605             enableBackgroundScanCommand(screenOn == false);
   1606         }
   1607 
   1608         if (screenOn) enableAllNetworks();
   1609         if (mUserWantsSuspendOpt.get()) {
   1610             if (screenOn) {
   1611                 sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0);
   1612             } else {
   1613                 //Allow 2s for suspend optimizations to be set
   1614                 mSuspendWakeLock.acquire(2000);
   1615                 sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0);
   1616             }
   1617         }
   1618         mScreenBroadcastReceived.set(true);
   1619     }
   1620 
   1621     private void checkAndSetConnectivityInstance() {
   1622         if (mCm == null) {
   1623             mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
   1624         }
   1625     }
   1626 
   1627     private boolean startTethering(ArrayList<String> available) {
   1628 
   1629         boolean wifiAvailable = false;
   1630 
   1631         checkAndSetConnectivityInstance();
   1632 
   1633         String[] wifiRegexs = mCm.getTetherableWifiRegexs();
   1634 
   1635         for (String intf : available) {
   1636             for (String regex : wifiRegexs) {
   1637                 if (intf.matches(regex)) {
   1638 
   1639                     InterfaceConfiguration ifcg = null;
   1640                     try {
   1641                         ifcg = mNwService.getInterfaceConfig(intf);
   1642                         if (ifcg != null) {
   1643                             /* IP/netmask: 192.168.43.1/255.255.255.0 */
   1644                             ifcg.setLinkAddress(new LinkAddress(
   1645                                     NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
   1646                             ifcg.setInterfaceUp();
   1647 
   1648                             mNwService.setInterfaceConfig(intf, ifcg);
   1649                         }
   1650                     } catch (Exception e) {
   1651                         loge("Error configuring interface " + intf + ", :" + e);
   1652                         return false;
   1653                     }
   1654 
   1655                     if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
   1656                         loge("Error tethering on " + intf);
   1657                         return false;
   1658                     }
   1659                     mTetherInterfaceName = intf;
   1660                     return true;
   1661                 }
   1662             }
   1663         }
   1664         // We found no interfaces to tether
   1665         return false;
   1666     }
   1667 
   1668     private void stopTethering() {
   1669 
   1670         checkAndSetConnectivityInstance();
   1671 
   1672         /* Clear the interface config to allow dhcp correctly configure new
   1673            ip settings */
   1674         InterfaceConfiguration ifcg = null;
   1675         try {
   1676             ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName);
   1677             if (ifcg != null) {
   1678                 ifcg.setLinkAddress(
   1679                         new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
   1680                 mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg);
   1681             }
   1682         } catch (Exception e) {
   1683             loge("Error resetting interface " + mTetherInterfaceName + ", :" + e);
   1684         }
   1685 
   1686         if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
   1687             loge("Untether initiate failed!");
   1688         }
   1689     }
   1690 
   1691     private boolean isWifiTethered(ArrayList<String> active) {
   1692 
   1693         checkAndSetConnectivityInstance();
   1694 
   1695         String[] wifiRegexs = mCm.getTetherableWifiRegexs();
   1696         for (String intf : active) {
   1697             for (String regex : wifiRegexs) {
   1698                 if (intf.matches(regex)) {
   1699                     return true;
   1700                 }
   1701             }
   1702         }
   1703         // We found no interfaces that are tethered
   1704         return false;
   1705     }
   1706 
   1707     /**
   1708      * Set the country code from the system setting value, if any.
   1709      */
   1710     private void setCountryCode() {
   1711         String countryCode = Settings.Global.getString(mContext.getContentResolver(),
   1712                 Settings.Global.WIFI_COUNTRY_CODE);
   1713         if (countryCode != null && !countryCode.isEmpty()) {
   1714             setCountryCode(countryCode, false);
   1715         } else {
   1716             //use driver default
   1717         }
   1718     }
   1719 
   1720     /**
   1721      * Set the frequency band from the system setting value, if any.
   1722      */
   1723     private void setFrequencyBand() {
   1724         int band = Settings.Global.getInt(mContext.getContentResolver(),
   1725                 Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
   1726         setFrequencyBand(band, false);
   1727     }
   1728 
   1729     private void setSuspendOptimizationsNative(int reason, boolean enabled) {
   1730         if (DBG) log("setSuspendOptimizationsNative: " + reason + " " + enabled);
   1731         if (enabled) {
   1732             mSuspendOptNeedsDisabled &= ~reason;
   1733             /* None of dhcp, screen or highperf need it disabled and user wants it enabled */
   1734             if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
   1735                 mWifiNative.setSuspendOptimizations(true);
   1736             }
   1737         } else {
   1738             mSuspendOptNeedsDisabled |= reason;
   1739             mWifiNative.setSuspendOptimizations(false);
   1740         }
   1741     }
   1742 
   1743     private void setSuspendOptimizations(int reason, boolean enabled) {
   1744         if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
   1745         if (enabled) {
   1746             mSuspendOptNeedsDisabled &= ~reason;
   1747         } else {
   1748             mSuspendOptNeedsDisabled |= reason;
   1749         }
   1750         if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
   1751     }
   1752 
   1753     private void setWifiState(int wifiState) {
   1754         final int previousWifiState = mWifiState.get();
   1755 
   1756         try {
   1757             if (wifiState == WIFI_STATE_ENABLED) {
   1758                 mBatteryStats.noteWifiOn();
   1759             } else if (wifiState == WIFI_STATE_DISABLED) {
   1760                 mBatteryStats.noteWifiOff();
   1761             }
   1762         } catch (RemoteException e) {
   1763             loge("Failed to note battery stats in wifi");
   1764         }
   1765 
   1766         mWifiState.set(wifiState);
   1767 
   1768         if (DBG) log("setWifiState: " + syncGetWifiStateByName());
   1769 
   1770         final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
   1771         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1772         intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
   1773         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
   1774         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1775     }
   1776 
   1777     private void setWifiApState(int wifiApState) {
   1778         final int previousWifiApState = mWifiApState.get();
   1779 
   1780         try {
   1781             if (wifiApState == WIFI_AP_STATE_ENABLED) {
   1782                 mBatteryStats.noteWifiOn();
   1783             } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
   1784                 mBatteryStats.noteWifiOff();
   1785             }
   1786         } catch (RemoteException e) {
   1787             loge("Failed to note battery stats in wifi");
   1788         }
   1789 
   1790         // Update state
   1791         mWifiApState.set(wifiApState);
   1792 
   1793         if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
   1794 
   1795         final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
   1796         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1797         intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
   1798         intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
   1799         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1800     }
   1801 
   1802     private static final String ID_STR = "id=";
   1803     private static final String BSSID_STR = "bssid=";
   1804     private static final String FREQ_STR = "freq=";
   1805     private static final String LEVEL_STR = "level=";
   1806     private static final String TSF_STR = "tsf=";
   1807     private static final String FLAGS_STR = "flags=";
   1808     private static final String SSID_STR = "ssid=";
   1809     private static final String DELIMITER_STR = "====";
   1810     private static final String END_STR = "####";
   1811 
   1812     /**
   1813      * Format:
   1814      *
   1815      * id=1
   1816      * bssid=68:7f:76:d7:1a:6e
   1817      * freq=2412
   1818      * level=-44
   1819      * tsf=1344626243700342
   1820      * flags=[WPA2-PSK-CCMP][WPS][ESS]
   1821      * ssid=zfdy
   1822      * ====
   1823      * id=2
   1824      * bssid=68:5f:74:d7:1a:6f
   1825      * freq=5180
   1826      * level=-73
   1827      * tsf=1344626243700373
   1828      * flags=[WPA2-PSK-CCMP][WPS][ESS]
   1829      * ssid=zuby
   1830      * ====
   1831      */
   1832     private void setScanResults() {
   1833         String bssid = "";
   1834         int level = 0;
   1835         int freq = 0;
   1836         long tsf = 0;
   1837         String flags = "";
   1838         WifiSsid wifiSsid = null;
   1839         String scanResults;
   1840         String tmpResults;
   1841         StringBuffer scanResultsBuf = new StringBuffer();
   1842         int sid = 0;
   1843 
   1844         while (true) {
   1845             tmpResults = mWifiNative.scanResults(sid);
   1846             if (TextUtils.isEmpty(tmpResults)) break;
   1847             scanResultsBuf.append(tmpResults);
   1848             scanResultsBuf.append("\n");
   1849             String[] lines = tmpResults.split("\n");
   1850             sid = -1;
   1851             for (int i=lines.length - 1; i >= 0; i--) {
   1852                 if (lines[i].startsWith(END_STR)) {
   1853                     break;
   1854                 } else if (lines[i].startsWith(ID_STR)) {
   1855                     try {
   1856                         sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
   1857                     } catch (NumberFormatException e) {
   1858                         // Nothing to do
   1859                     }
   1860                     break;
   1861                 }
   1862             }
   1863             if (sid == -1) break;
   1864         }
   1865 
   1866         scanResults = scanResultsBuf.toString();
   1867         if (TextUtils.isEmpty(scanResults)) {
   1868            return;
   1869         }
   1870 
   1871         synchronized(mScanResultCache) {
   1872             mScanResults = new ArrayList<ScanResult>();
   1873             String[] lines = scanResults.split("\n");
   1874             final int bssidStrLen = BSSID_STR.length();
   1875             final int flagLen = FLAGS_STR.length();
   1876 
   1877             for (String line : lines) {
   1878                 if (line.startsWith(BSSID_STR)) {
   1879                     bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
   1880                 } else if (line.startsWith(FREQ_STR)) {
   1881                     try {
   1882                         freq = Integer.parseInt(line.substring(FREQ_STR.length()));
   1883                     } catch (NumberFormatException e) {
   1884                         freq = 0;
   1885                     }
   1886                 } else if (line.startsWith(LEVEL_STR)) {
   1887                     try {
   1888                         level = Integer.parseInt(line.substring(LEVEL_STR.length()));
   1889                         /* some implementations avoid negative values by adding 256
   1890                          * so we need to adjust for that here.
   1891                          */
   1892                         if (level > 0) level -= 256;
   1893                     } catch(NumberFormatException e) {
   1894                         level = 0;
   1895                     }
   1896                 } else if (line.startsWith(TSF_STR)) {
   1897                     try {
   1898                         tsf = Long.parseLong(line.substring(TSF_STR.length()));
   1899                     } catch (NumberFormatException e) {
   1900                         tsf = 0;
   1901                     }
   1902                 } else if (line.startsWith(FLAGS_STR)) {
   1903                     flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
   1904                 } else if (line.startsWith(SSID_STR)) {
   1905                     wifiSsid = WifiSsid.createFromAsciiEncoded(
   1906                             line.substring(SSID_STR.length()));
   1907                 } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
   1908                     if (bssid != null) {
   1909                         String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
   1910                         String key = bssid + ssid;
   1911                         ScanResult scanResult = mScanResultCache.get(key);
   1912                         if (scanResult != null) {
   1913                             scanResult.level = level;
   1914                             scanResult.wifiSsid = wifiSsid;
   1915                             // Keep existing API
   1916                             scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
   1917                                     WifiSsid.NONE;
   1918                             scanResult.capabilities = flags;
   1919                             scanResult.frequency = freq;
   1920                             scanResult.timestamp = tsf;
   1921                         } else {
   1922                             scanResult =
   1923                                 new ScanResult(
   1924                                         wifiSsid, bssid, flags, level, freq, tsf);
   1925                             mScanResultCache.put(key, scanResult);
   1926                         }
   1927                         mScanResults.add(scanResult);
   1928                     }
   1929                     bssid = null;
   1930                     level = 0;
   1931                     freq = 0;
   1932                     tsf = 0;
   1933                     flags = "";
   1934                     wifiSsid = null;
   1935                 }
   1936             }
   1937         }
   1938     }
   1939 
   1940     /*
   1941      * Fetch RSSI and linkspeed on current connection
   1942      */
   1943     private void fetchRssiAndLinkSpeedNative() {
   1944         int newRssi = -1;
   1945         int newLinkSpeed = -1;
   1946 
   1947         String signalPoll = mWifiNative.signalPoll();
   1948 
   1949         if (signalPoll != null) {
   1950             String[] lines = signalPoll.split("\n");
   1951             for (String line : lines) {
   1952                 String[] prop = line.split("=");
   1953                 if (prop.length < 2) continue;
   1954                 try {
   1955                     if (prop[0].equals("RSSI")) {
   1956                         newRssi = Integer.parseInt(prop[1]);
   1957                     } else if (prop[0].equals("LINKSPEED")) {
   1958                         newLinkSpeed = Integer.parseInt(prop[1]);
   1959                     }
   1960                 } catch (NumberFormatException e) {
   1961                     //Ignore, defaults on rssi and linkspeed are assigned
   1962                 }
   1963             }
   1964         }
   1965 
   1966         if (newRssi != -1 && MIN_RSSI < newRssi && newRssi < MAX_RSSI) { // screen out invalid values
   1967             /* some implementations avoid negative values by adding 256
   1968              * so we need to adjust for that here.
   1969              */
   1970             if (newRssi > 0) newRssi -= 256;
   1971             mWifiInfo.setRssi(newRssi);
   1972             /*
   1973              * Rather then sending the raw RSSI out every time it
   1974              * changes, we precalculate the signal level that would
   1975              * be displayed in the status bar, and only send the
   1976              * broadcast if that much more coarse-grained number
   1977              * changes. This cuts down greatly on the number of
   1978              * broadcasts, at the cost of not informing others
   1979              * interested in RSSI of all the changes in signal
   1980              * level.
   1981              */
   1982             int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
   1983             if (newSignalLevel != mLastSignalLevel) {
   1984                 sendRssiChangeBroadcast(newRssi);
   1985             }
   1986             mLastSignalLevel = newSignalLevel;
   1987         } else {
   1988             mWifiInfo.setRssi(MIN_RSSI);
   1989         }
   1990 
   1991         if (newLinkSpeed != -1) {
   1992             mWifiInfo.setLinkSpeed(newLinkSpeed);
   1993         }
   1994     }
   1995 
   1996     /*
   1997      * Fetch TX packet counters on current connection
   1998      */
   1999     private void fetchPktcntNative(RssiPacketCountInfo info) {
   2000         String pktcntPoll = mWifiNative.pktcntPoll();
   2001 
   2002         if (pktcntPoll != null) {
   2003             String[] lines = pktcntPoll.split("\n");
   2004             for (String line : lines) {
   2005                 String[] prop = line.split("=");
   2006                 if (prop.length < 2) continue;
   2007                 try {
   2008                     if (prop[0].equals("TXGOOD")) {
   2009                         info.txgood = Integer.parseInt(prop[1]);
   2010                     } else if (prop[0].equals("TXBAD")) {
   2011                         info.txbad = Integer.parseInt(prop[1]);
   2012                     }
   2013                 } catch (NumberFormatException e) {
   2014                     //Ignore
   2015                 }
   2016             }
   2017         }
   2018     }
   2019 
   2020     /**
   2021      * Updates mLinkProperties by merging information from various sources.
   2022      *
   2023      * This is needed because the information in mLinkProperties comes from multiple sources (DHCP,
   2024      * netlink, static configuration, ...). When one of these sources of information has updated
   2025      * link properties, we can't just assign them to mLinkProperties or we'd lose track of the
   2026      * information that came from other sources. Instead, when one of those sources has new
   2027      * information, we update the object that tracks the information from that source and then
   2028      * call this method to apply the change to mLinkProperties.
   2029      *
   2030      * The information in mLinkProperties is currently obtained as follows:
   2031      * - Interface name: set in the constructor.
   2032      * - IPv4 and IPv6 addresses: netlink, via mInterfaceObserver.
   2033      * - IPv4 routes, DNS servers, and domains: DHCP.
   2034      * - HTTP proxy: the wifi config store.
   2035      */
   2036     private void updateLinkProperties() {
   2037         LinkProperties newLp = new LinkProperties();
   2038 
   2039         // Interface name and proxy are locally configured.
   2040         newLp.setInterfaceName(mInterfaceName);
   2041         newLp.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
   2042 
   2043         // IPv4 and IPv6 addresses come from netlink.
   2044         newLp.setLinkAddresses(mNetlinkLinkProperties.getLinkAddresses());
   2045 
   2046         // For now, routing and DNS only come from DHCP or static configuration. In the future,
   2047         // we'll need to merge IPv6 DNS servers and domains coming from netlink.
   2048         synchronized (mDhcpResultsLock) {
   2049             // Even when we're using static configuration, we don't need to look at the config
   2050             // store, because static IP configuration also populates mDhcpResults.
   2051             if ((mDhcpResults != null) && (mDhcpResults.linkProperties != null)) {
   2052                 LinkProperties lp = mDhcpResults.linkProperties;
   2053                 for (RouteInfo route: lp.getRoutes()) {
   2054                     newLp.addRoute(route);
   2055                 }
   2056                 for (InetAddress dns: lp.getDnses()) {
   2057                     newLp.addDns(dns);
   2058                 }
   2059                 newLp.setDomains(lp.getDomains());
   2060             }
   2061         }
   2062 
   2063         // If anything has changed, and we're already connected, send out a notification.
   2064         // If we're still connecting, apps will be notified when we connect.
   2065         if (!newLp.equals(mLinkProperties)) {
   2066             if (DBG) {
   2067                 log("Link configuration changed for netId: " + mLastNetworkId
   2068                         + " old: " + mLinkProperties + "new: " + newLp);
   2069             }
   2070             mLinkProperties = newLp;
   2071             if (getNetworkDetailedState() == DetailedState.CONNECTED) {
   2072                 sendLinkConfigurationChangedBroadcast();
   2073             }
   2074         }
   2075     }
   2076 
   2077     /**
   2078      * Clears all our link properties.
   2079      */
   2080     private void clearLinkProperties() {
   2081         // If the network used DHCP, clear the LinkProperties we stored in the config store.
   2082         if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
   2083             mWifiConfigStore.clearLinkProperties(mLastNetworkId);
   2084         }
   2085 
   2086         // Clear the link properties obtained from DHCP and netlink.
   2087         synchronized(mDhcpResultsLock) {
   2088             if (mDhcpResults != null && mDhcpResults.linkProperties != null) {
   2089                 mDhcpResults.linkProperties.clear();
   2090             }
   2091         }
   2092         mNetlinkLinkProperties.clear();
   2093 
   2094         // Now clear the merged link properties.
   2095         mLinkProperties.clear();
   2096     }
   2097 
   2098     private int getMaxDhcpRetries() {
   2099         return Settings.Global.getInt(mContext.getContentResolver(),
   2100                                       Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
   2101                                       DEFAULT_MAX_DHCP_RETRIES);
   2102     }
   2103 
   2104     private void sendScanResultsAvailableBroadcast() {
   2105         noteScanEnd();
   2106         Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
   2107         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   2108         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
   2109     }
   2110 
   2111     private void sendRssiChangeBroadcast(final int newRssi) {
   2112         Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
   2113         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   2114         intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
   2115         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   2116     }
   2117 
   2118     private void sendNetworkStateChangeBroadcast(String bssid) {
   2119         Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
   2120         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   2121         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
   2122         intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
   2123         if (bssid != null)
   2124             intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
   2125         if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
   2126                 mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
   2127             intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
   2128         }
   2129         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   2130     }
   2131 
   2132     private void sendLinkConfigurationChangedBroadcast() {
   2133         Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
   2134         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   2135         intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
   2136         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
   2137     }
   2138 
   2139     private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
   2140         Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
   2141         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   2142         intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
   2143         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
   2144     }
   2145 
   2146     /**
   2147      * Record the detailed state of a network.
   2148      * @param state the new {@code DetailedState}
   2149      */
   2150     private void setNetworkDetailedState(NetworkInfo.DetailedState state) {
   2151         if (DBG) {
   2152             log("setDetailed state, old ="
   2153                     + mNetworkInfo.getDetailedState() + " and new state=" + state);
   2154         }
   2155 
   2156         if (state != mNetworkInfo.getDetailedState()) {
   2157             mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
   2158         }
   2159     }
   2160 
   2161     private DetailedState getNetworkDetailedState() {
   2162         return mNetworkInfo.getDetailedState();
   2163     }
   2164 
   2165 
   2166     private SupplicantState handleSupplicantStateChange(Message message) {
   2167         StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
   2168         SupplicantState state = stateChangeResult.state;
   2169         // Supplicant state change
   2170         // [31-13] Reserved for future use
   2171         // [8 - 0] Supplicant state (as defined in SupplicantState.java)
   2172         // 50023 supplicant_state_changed (custom|1|5)
   2173         mWifiInfo.setSupplicantState(state);
   2174         // Network id is only valid when we start connecting
   2175         if (SupplicantState.isConnecting(state)) {
   2176             mWifiInfo.setNetworkId(stateChangeResult.networkId);
   2177         } else {
   2178             mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
   2179         }
   2180 
   2181         mWifiInfo.setBSSID(stateChangeResult.BSSID);
   2182         mWifiInfo.setSSID(stateChangeResult.wifiSsid);
   2183 
   2184         mSupplicantStateTracker.sendMessage(Message.obtain(message));
   2185 
   2186         return state;
   2187     }
   2188 
   2189     /**
   2190      * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
   2191      * using the interface, stopping DHCP & disabling interface
   2192      */
   2193     private void handleNetworkDisconnect() {
   2194         if (DBG) log("Stopping DHCP and clearing IP");
   2195 
   2196         stopDhcp();
   2197 
   2198         try {
   2199             mNwService.clearInterfaceAddresses(mInterfaceName);
   2200             mNwService.disableIpv6(mInterfaceName);
   2201         } catch (Exception e) {
   2202             loge("Failed to clear addresses or disable ipv6" + e);
   2203         }
   2204 
   2205         /* Reset data structures */
   2206         mWifiInfo.setInetAddress(null);
   2207         mWifiInfo.setBSSID(null);
   2208         mWifiInfo.setSSID(null);
   2209         mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
   2210         mWifiInfo.setRssi(MIN_RSSI);
   2211         mWifiInfo.setLinkSpeed(-1);
   2212         mWifiInfo.setMeteredHint(false);
   2213 
   2214         setNetworkDetailedState(DetailedState.DISCONNECTED);
   2215         mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
   2216 
   2217         /* Clear network properties */
   2218         clearLinkProperties();
   2219 
   2220         /* send event to CM & network change broadcast */
   2221         sendNetworkStateChangeBroadcast(mLastBssid);
   2222 
   2223         mLastBssid= null;
   2224         mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
   2225     }
   2226 
   2227     private void handleSupplicantConnectionLoss() {
   2228         /* Socket connection can be lost when we do a graceful shutdown
   2229         * or when the driver is hung. Ensure supplicant is stopped here.
   2230         */
   2231         mWifiMonitor.killSupplicant(mP2pSupported);
   2232         sendSupplicantConnectionChangedBroadcast(false);
   2233         setWifiState(WIFI_STATE_DISABLED);
   2234     }
   2235 
   2236     void handlePreDhcpSetup() {
   2237         mDhcpActive = true;
   2238         if (!mBluetoothConnectionActive) {
   2239             /*
   2240              * There are problems setting the Wi-Fi driver's power
   2241              * mode to active when bluetooth coexistence mode is
   2242              * enabled or sense.
   2243              * <p>
   2244              * We set Wi-Fi to active mode when
   2245              * obtaining an IP address because we've found
   2246              * compatibility issues with some routers with low power
   2247              * mode.
   2248              * <p>
   2249              * In order for this active power mode to properly be set,
   2250              * we disable coexistence mode until we're done with
   2251              * obtaining an IP address.  One exception is if we
   2252              * are currently connected to a headset, since disabling
   2253              * coexistence would interrupt that connection.
   2254              */
   2255             // Disable the coexistence mode
   2256             mWifiNative.setBluetoothCoexistenceMode(
   2257                     mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
   2258         }
   2259 
   2260         /* Disable power save and suspend optimizations during DHCP */
   2261         // Note: The order here is important for now. Brcm driver changes
   2262         // power settings when we control suspend mode optimizations.
   2263         // TODO: Remove this comment when the driver is fixed.
   2264         setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
   2265         mWifiNative.setPowerSave(false);
   2266 
   2267         stopBatchedScan();
   2268 
   2269         /* P2p discovery breaks dhcp, shut it down in order to get through this */
   2270         Message msg = new Message();
   2271         msg.what = WifiP2pService.BLOCK_DISCOVERY;
   2272         msg.arg1 = WifiP2pService.ENABLED;
   2273         msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
   2274         msg.obj = mDhcpStateMachine;
   2275         mWifiP2pChannel.sendMessage(msg);
   2276     }
   2277 
   2278 
   2279     void startDhcp() {
   2280         if (mDhcpStateMachine == null) {
   2281             mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
   2282                     mContext, WifiStateMachine.this, mInterfaceName);
   2283 
   2284         }
   2285         mDhcpStateMachine.registerForPreDhcpNotification();
   2286         mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
   2287     }
   2288 
   2289     void stopDhcp() {
   2290         if (mDhcpStateMachine != null) {
   2291             /* In case we were in middle of DHCP operation restore back powermode */
   2292             handlePostDhcpSetup();
   2293             mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
   2294         }
   2295     }
   2296 
   2297     void handlePostDhcpSetup() {
   2298         /* Restore power save and suspend optimizations */
   2299         setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
   2300         mWifiNative.setPowerSave(true);
   2301 
   2302         mWifiP2pChannel.sendMessage(WifiP2pService.BLOCK_DISCOVERY, WifiP2pService.DISABLED);
   2303 
   2304         // Set the coexistence mode back to its default value
   2305         mWifiNative.setBluetoothCoexistenceMode(
   2306                 mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
   2307 
   2308         mDhcpActive = false;
   2309 
   2310         if (mBatchedScanSettings != null) {
   2311             startBatchedScan();
   2312         }
   2313     }
   2314 
   2315     private void handleSuccessfulIpConfiguration(DhcpResults dhcpResults) {
   2316         mLastSignalLevel = -1; // force update of signal strength
   2317         mReconnectCount = 0; //Reset IP failure tracking
   2318         synchronized (mDhcpResultsLock) {
   2319             mDhcpResults = dhcpResults;
   2320         }
   2321         LinkProperties linkProperties = dhcpResults.linkProperties;
   2322         mWifiConfigStore.setLinkProperties(mLastNetworkId, new LinkProperties(linkProperties));
   2323         InetAddress addr = null;
   2324         Iterator<InetAddress> addrs = linkProperties.getAddresses().iterator();
   2325         if (addrs.hasNext()) {
   2326             addr = addrs.next();
   2327         }
   2328         mWifiInfo.setInetAddress(addr);
   2329         mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
   2330         updateLinkProperties();
   2331     }
   2332 
   2333     private void handleFailedIpConfiguration() {
   2334         loge("IP configuration failed");
   2335 
   2336         mWifiInfo.setInetAddress(null);
   2337         mWifiInfo.setMeteredHint(false);
   2338         /**
   2339          * If we've exceeded the maximum number of retries for DHCP
   2340          * to a given network, disable the network
   2341          */
   2342         int maxRetries = getMaxDhcpRetries();
   2343         // maxRetries == 0 means keep trying forever
   2344         if (maxRetries > 0 && ++mReconnectCount > maxRetries) {
   2345             loge("Failed " +
   2346                     mReconnectCount + " times, Disabling " + mLastNetworkId);
   2347             mWifiConfigStore.disableNetwork(mLastNetworkId,
   2348                     WifiConfiguration.DISABLED_DHCP_FAILURE);
   2349             mReconnectCount = 0;
   2350         }
   2351 
   2352         /* DHCP times out after about 30 seconds, we do a
   2353          * disconnect and an immediate reconnect to try again
   2354          */
   2355         mWifiNative.disconnect();
   2356         mWifiNative.reconnect();
   2357     }
   2358 
   2359     /* Current design is to not set the config on a running hostapd but instead
   2360      * stop and start tethering when user changes config on a running access point
   2361      *
   2362      * TODO: Add control channel setup through hostapd that allows changing config
   2363      * on a running daemon
   2364      */
   2365     private void startSoftApWithConfig(final WifiConfiguration config) {
   2366         // start hostapd on a seperate thread
   2367         new Thread(new Runnable() {
   2368             public void run() {
   2369                 try {
   2370                     mNwService.startAccessPoint(config, mInterfaceName);
   2371                 } catch (Exception e) {
   2372                     loge("Exception in softap start " + e);
   2373                     try {
   2374                         mNwService.stopAccessPoint(mInterfaceName);
   2375                         mNwService.startAccessPoint(config, mInterfaceName);
   2376                     } catch (Exception e1) {
   2377                         loge("Exception in softap re-start " + e1);
   2378                         sendMessage(CMD_START_AP_FAILURE);
   2379                         return;
   2380                     }
   2381                 }
   2382                 if (DBG) log("Soft AP start successful");
   2383                 sendMessage(CMD_START_AP_SUCCESS);
   2384             }
   2385         }).start();
   2386     }
   2387 
   2388     /********************************************************
   2389      * HSM states
   2390      *******************************************************/
   2391 
   2392     class DefaultState extends State {
   2393         @Override
   2394         public boolean processMessage(Message message) {
   2395             switch (message.what) {
   2396                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
   2397                     if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
   2398                         mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
   2399                     } else {
   2400                         loge("WifiP2pService connection failure, error=" + message.arg1);
   2401                     }
   2402                     break;
   2403                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
   2404                     loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
   2405                     //TODO: Re-establish connection to state machine after a delay
   2406                     //mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
   2407                     break;
   2408                 case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
   2409                     mBluetoothConnectionActive = (message.arg1 !=
   2410                             BluetoothAdapter.STATE_DISCONNECTED);
   2411                     break;
   2412                     /* Synchronous call returns */
   2413                 case CMD_PING_SUPPLICANT:
   2414                 case CMD_ENABLE_NETWORK:
   2415                 case CMD_ADD_OR_UPDATE_NETWORK:
   2416                 case CMD_REMOVE_NETWORK:
   2417                 case CMD_SAVE_CONFIG:
   2418                     replyToMessage(message, message.what, FAILURE);
   2419                     break;
   2420                 case CMD_GET_CONFIGURED_NETWORKS:
   2421                     replyToMessage(message, message.what, (List<WifiConfiguration>) null);
   2422                     break;
   2423                 case CMD_ENABLE_RSSI_POLL:
   2424                     mEnableRssiPolling = (message.arg1 == 1);
   2425                     break;
   2426                 case CMD_ENABLE_BACKGROUND_SCAN:
   2427                     mEnableBackgroundScan = (message.arg1 == 1);
   2428                     break;
   2429                 case CMD_SET_HIGH_PERF_MODE:
   2430                     if (message.arg1 == 1) {
   2431                         setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
   2432                     } else {
   2433                         setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
   2434                     }
   2435                     break;
   2436                 case CMD_BOOT_COMPLETED:
   2437                     String countryCode = mPersistedCountryCode;
   2438                     if (TextUtils.isEmpty(countryCode) == false) {
   2439                         Settings.Global.putString(mContext.getContentResolver(),
   2440                                 Settings.Global.WIFI_COUNTRY_CODE,
   2441                                 countryCode);
   2442                         // it may be that the state transition that should send this info
   2443                         // to the driver happened between mPersistedCountryCode getting set
   2444                         // and now, so simply persisting it here would mean we have sent
   2445                         // nothing to the driver.  Send the cmd so it might be set now.
   2446                         sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE, countryCode);
   2447                     }
   2448                     break;
   2449                 case CMD_SET_BATCHED_SCAN:
   2450                     recordBatchedScanSettings((BatchedScanSettings)message.obj);
   2451                     break;
   2452                 case CMD_POLL_BATCHED_SCAN:
   2453                     handleBatchedScanPollRequest();
   2454                     break;
   2455                 case CMD_START_NEXT_BATCHED_SCAN:
   2456                     startNextBatchedScan();
   2457                     break;
   2458                     /* Discard */
   2459                 case CMD_START_SCAN:
   2460                 case CMD_START_SUPPLICANT:
   2461                 case CMD_STOP_SUPPLICANT:
   2462                 case CMD_STOP_SUPPLICANT_FAILED:
   2463                 case CMD_START_DRIVER:
   2464                 case CMD_STOP_DRIVER:
   2465                 case CMD_DELAYED_STOP_DRIVER:
   2466                 case CMD_DRIVER_START_TIMED_OUT:
   2467                 case CMD_CAPTIVE_CHECK_COMPLETE:
   2468                 case CMD_START_AP:
   2469                 case CMD_START_AP_SUCCESS:
   2470                 case CMD_START_AP_FAILURE:
   2471                 case CMD_STOP_AP:
   2472                 case CMD_TETHER_STATE_CHANGE:
   2473                 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
   2474                 case CMD_DISCONNECT:
   2475                 case CMD_RECONNECT:
   2476                 case CMD_REASSOCIATE:
   2477                 case CMD_RELOAD_TLS_AND_RECONNECT:
   2478                 case WifiMonitor.SUP_CONNECTION_EVENT:
   2479                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
   2480                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
   2481                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
   2482                 case WifiMonitor.SCAN_RESULTS_EVENT:
   2483                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
   2484                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
   2485                 case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
   2486                 case WifiMonitor.WPS_OVERLAP_EVENT:
   2487                 case CMD_BLACKLIST_NETWORK:
   2488                 case CMD_CLEAR_BLACKLIST:
   2489                 case CMD_SET_OPERATIONAL_MODE:
   2490                 case CMD_SET_COUNTRY_CODE:
   2491                 case CMD_SET_FREQUENCY_BAND:
   2492                 case CMD_RSSI_POLL:
   2493                 case CMD_ENABLE_ALL_NETWORKS:
   2494                 case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
   2495                 case DhcpStateMachine.CMD_POST_DHCP_ACTION:
   2496                 /* Handled by WifiApConfigStore */
   2497                 case CMD_SET_AP_CONFIG:
   2498                 case CMD_SET_AP_CONFIG_COMPLETED:
   2499                 case CMD_REQUEST_AP_CONFIG:
   2500                 case CMD_RESPONSE_AP_CONFIG:
   2501                 case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
   2502                 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
   2503                 case CMD_NO_NETWORKS_PERIODIC_SCAN:
   2504                 case CMD_DISABLE_P2P_RSP:
   2505                     break;
   2506                 case DhcpStateMachine.CMD_ON_QUIT:
   2507                     mDhcpStateMachine = null;
   2508                     break;
   2509                 case CMD_SET_SUSPEND_OPT_ENABLED:
   2510                     if (message.arg1 == 1) {
   2511                         mSuspendWakeLock.release();
   2512                         setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
   2513                     } else {
   2514                         setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
   2515                     }
   2516                     break;
   2517                 case WifiMonitor.DRIVER_HUNG_EVENT:
   2518                     setSupplicantRunning(false);
   2519                     setSupplicantRunning(true);
   2520                     break;
   2521                 case WifiManager.CONNECT_NETWORK:
   2522                     replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
   2523                             WifiManager.BUSY);
   2524                     break;
   2525                 case WifiManager.FORGET_NETWORK:
   2526                     replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
   2527                             WifiManager.BUSY);
   2528                     break;
   2529                 case WifiManager.SAVE_NETWORK:
   2530                     replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
   2531                             WifiManager.BUSY);
   2532                     break;
   2533                 case WifiManager.START_WPS:
   2534                     replyToMessage(message, WifiManager.WPS_FAILED,
   2535                             WifiManager.BUSY);
   2536                     break;
   2537                 case WifiManager.CANCEL_WPS:
   2538                     replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
   2539                             WifiManager.BUSY);
   2540                     break;
   2541                 case WifiManager.DISABLE_NETWORK:
   2542                     replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
   2543                             WifiManager.BUSY);
   2544                     break;
   2545                 case WifiManager.RSSI_PKTCNT_FETCH:
   2546                     replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
   2547                             WifiManager.BUSY);
   2548                     break;
   2549                 case WifiP2pService.P2P_CONNECTION_CHANGED:
   2550                     NetworkInfo info = (NetworkInfo) message.obj;
   2551                     mP2pConnected.set(info.isConnected());
   2552                     break;
   2553                 case WifiP2pService.DISCONNECT_WIFI_REQUEST:
   2554                     mTemporarilyDisconnectWifi = (message.arg1 == 1);
   2555                     replyToMessage(message, WifiP2pService.DISCONNECT_WIFI_RESPONSE);
   2556                     break;
   2557                 case CMD_IP_ADDRESS_UPDATED:
   2558                     // addLinkAddress is a no-op if called more than once with the same address.
   2559                     if (mNetlinkLinkProperties.addLinkAddress((LinkAddress) message.obj)) {
   2560                         updateLinkProperties();
   2561                     }
   2562                     break;
   2563                 case CMD_IP_ADDRESS_REMOVED:
   2564                     if (mNetlinkLinkProperties.removeLinkAddress((LinkAddress) message.obj)) {
   2565                         updateLinkProperties();
   2566                     }
   2567                     break;
   2568                 default:
   2569                     loge("Error! unhandled message" + message);
   2570                     break;
   2571             }
   2572             return HANDLED;
   2573         }
   2574     }
   2575 
   2576     class InitialState extends State {
   2577         @Override
   2578         public void enter() {
   2579             mWifiNative.unloadDriver();
   2580 
   2581             if (mWifiP2pChannel == null) {
   2582                 mWifiP2pChannel = new AsyncChannel();
   2583                 mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
   2584             }
   2585 
   2586             if (mWifiApConfigChannel == null) {
   2587                 mWifiApConfigChannel = new AsyncChannel();
   2588                 WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
   2589                         mContext, getHandler());
   2590                 wifiApConfigStore.loadApConfiguration();
   2591                 mWifiApConfigChannel.connectSync(mContext, getHandler(),
   2592                         wifiApConfigStore.getMessenger());
   2593             }
   2594         }
   2595         @Override
   2596         public boolean processMessage(Message message) {
   2597             switch (message.what) {
   2598                 case CMD_START_SUPPLICANT:
   2599                     if (mWifiNative.loadDriver()) {
   2600                         try {
   2601                             mNwService.wifiFirmwareReload(mInterfaceName, "STA");
   2602                         } catch (Exception e) {
   2603                             loge("Failed to reload STA firmware " + e);
   2604                             // continue
   2605                         }
   2606 
   2607                         try {
   2608                             // A runtime crash can leave the interface up and
   2609                             // this affects connectivity when supplicant starts up.
   2610                             // Ensure interface is down before a supplicant start.
   2611                             mNwService.setInterfaceDown(mInterfaceName);
   2612                             // Set privacy extensions
   2613                             mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
   2614 
   2615                            // IPv6 is enabled only as long as access point is connected since:
   2616                            // - IPv6 addresses and routes stick around after disconnection
   2617                            // - kernel is unaware when connected and fails to start IPv6 negotiation
   2618                            // - kernel can start autoconfiguration when 802.1x is not complete
   2619                             mNwService.disableIpv6(mInterfaceName);
   2620                         } catch (RemoteException re) {
   2621                             loge("Unable to change interface settings: " + re);
   2622                         } catch (IllegalStateException ie) {
   2623                             loge("Unable to change interface settings: " + ie);
   2624                         }
   2625 
   2626                        /* Stop a running supplicant after a runtime restart
   2627                         * Avoids issues with drivers that do not handle interface down
   2628                         * on a running supplicant properly.
   2629                         */
   2630                         mWifiMonitor.killSupplicant(mP2pSupported);
   2631                         if(mWifiNative.startSupplicant(mP2pSupported)) {
   2632                             setWifiState(WIFI_STATE_ENABLING);
   2633                             if (DBG) log("Supplicant start successful");
   2634                             mWifiMonitor.startMonitoring();
   2635                             transitionTo(mSupplicantStartingState);
   2636                         } else {
   2637                             loge("Failed to start supplicant!");
   2638                         }
   2639                     } else {
   2640                         loge("Failed to load driver");
   2641                     }
   2642                     break;
   2643                 case CMD_START_AP:
   2644                     if (mWifiNative.loadDriver()) {
   2645                         setWifiApState(WIFI_AP_STATE_ENABLING);
   2646                         transitionTo(mSoftApStartingState);
   2647                     } else {
   2648                         loge("Failed to load driver for softap");
   2649                     }
   2650                 default:
   2651                     return NOT_HANDLED;
   2652             }
   2653             return HANDLED;
   2654         }
   2655     }
   2656 
   2657     class SupplicantStartingState extends State {
   2658         private void initializeWpsDetails() {
   2659             String detail;
   2660             detail = SystemProperties.get("ro.product.name", "");
   2661             if (!mWifiNative.setDeviceName(detail)) {
   2662                 loge("Failed to set device name " +  detail);
   2663             }
   2664             detail = SystemProperties.get("ro.product.manufacturer", "");
   2665             if (!mWifiNative.setManufacturer(detail)) {
   2666                 loge("Failed to set manufacturer " + detail);
   2667             }
   2668             detail = SystemProperties.get("ro.product.model", "");
   2669             if (!mWifiNative.setModelName(detail)) {
   2670                 loge("Failed to set model name " + detail);
   2671             }
   2672             detail = SystemProperties.get("ro.product.model", "");
   2673             if (!mWifiNative.setModelNumber(detail)) {
   2674                 loge("Failed to set model number " + detail);
   2675             }
   2676             detail = SystemProperties.get("ro.serialno", "");
   2677             if (!mWifiNative.setSerialNumber(detail)) {
   2678                 loge("Failed to set serial number " + detail);
   2679             }
   2680             if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
   2681                 loge("Failed to set WPS config methods");
   2682             }
   2683             if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
   2684                 loge("Failed to set primary device type " + mPrimaryDeviceType);
   2685             }
   2686         }
   2687 
   2688         @Override
   2689         public boolean processMessage(Message message) {
   2690             switch(message.what) {
   2691                 case WifiMonitor.SUP_CONNECTION_EVENT:
   2692                     if (DBG) log("Supplicant connection established");
   2693                     setWifiState(WIFI_STATE_ENABLED);
   2694                     mSupplicantRestartCount = 0;
   2695                     /* Reset the supplicant state to indicate the supplicant
   2696                      * state is not known at this time */
   2697                     mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
   2698                     /* Initialize data structures */
   2699                     mLastBssid = null;
   2700                     mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
   2701                     mLastSignalLevel = -1;
   2702 
   2703                     mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
   2704                     mWifiConfigStore.loadAndEnableAllNetworks();
   2705                     initializeWpsDetails();
   2706 
   2707                     sendSupplicantConnectionChangedBroadcast(true);
   2708                     transitionTo(mDriverStartedState);
   2709                     break;
   2710                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
   2711                     if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
   2712                         loge("Failed to setup control channel, restart supplicant");
   2713                         mWifiMonitor.killSupplicant(mP2pSupported);
   2714                         transitionTo(mInitialState);
   2715                         sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
   2716                     } else {
   2717                         loge("Failed " + mSupplicantRestartCount +
   2718                                 " times to start supplicant, unload driver");
   2719                         mSupplicantRestartCount = 0;
   2720                         setWifiState(WIFI_STATE_UNKNOWN);
   2721                         transitionTo(mInitialState);
   2722                     }
   2723                     break;
   2724                 case CMD_START_SUPPLICANT:
   2725                 case CMD_STOP_SUPPLICANT:
   2726                 case CMD_START_AP:
   2727                 case CMD_STOP_AP:
   2728                 case CMD_START_DRIVER:
   2729                 case CMD_STOP_DRIVER:
   2730                 case CMD_SET_OPERATIONAL_MODE:
   2731                 case CMD_SET_COUNTRY_CODE:
   2732                 case CMD_SET_FREQUENCY_BAND:
   2733                 case CMD_START_PACKET_FILTERING:
   2734                 case CMD_STOP_PACKET_FILTERING:
   2735                     deferMessage(message);
   2736                     break;
   2737                 default:
   2738                     return NOT_HANDLED;
   2739             }
   2740             return HANDLED;
   2741         }
   2742     }
   2743 
   2744     class SupplicantStartedState extends State {
   2745         @Override
   2746         public void enter() {
   2747             /* Wifi is available as long as we have a connection to supplicant */
   2748             mNetworkInfo.setIsAvailable(true);
   2749 
   2750             int defaultInterval = mContext.getResources().getInteger(
   2751                     R.integer.config_wifi_supplicant_scan_interval);
   2752 
   2753             mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
   2754                     Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
   2755                     defaultInterval);
   2756 
   2757             mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
   2758         }
   2759         @Override
   2760         public boolean processMessage(Message message) {
   2761             switch(message.what) {
   2762                 case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
   2763                     if (mP2pSupported) {
   2764                         transitionTo(mWaitForP2pDisableState);
   2765                     } else {
   2766                         transitionTo(mSupplicantStoppingState);
   2767                     }
   2768                     break;
   2769                 case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
   2770                     loge("Connection lost, restart supplicant");
   2771                     handleSupplicantConnectionLoss();
   2772                     handleNetworkDisconnect();
   2773                     mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
   2774                     if (mP2pSupported) {
   2775                         transitionTo(mWaitForP2pDisableState);
   2776                     } else {
   2777                         transitionTo(mInitialState);
   2778                     }
   2779                     sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
   2780                     break;
   2781                 case WifiMonitor.SCAN_RESULTS_EVENT:
   2782                     setScanResults();
   2783                     sendScanResultsAvailableBroadcast();
   2784                     mScanResultIsPending = false;
   2785                     break;
   2786                 case CMD_PING_SUPPLICANT:
   2787                     boolean ok = mWifiNative.ping();
   2788                     replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
   2789                     break;
   2790                     /* Cannot start soft AP while in client mode */
   2791                 case CMD_START_AP:
   2792                     loge("Failed to start soft AP with a running supplicant");
   2793                     setWifiApState(WIFI_AP_STATE_FAILED);
   2794                     break;
   2795                 case CMD_SET_OPERATIONAL_MODE:
   2796                     mOperationalMode = message.arg1;
   2797                     break;
   2798                 default:
   2799                     return NOT_HANDLED;
   2800             }
   2801             return HANDLED;
   2802         }
   2803 
   2804         @Override
   2805         public void exit() {
   2806             mNetworkInfo.setIsAvailable(false);
   2807         }
   2808     }
   2809 
   2810     class SupplicantStoppingState extends State {
   2811         @Override
   2812         public void enter() {
   2813             /* Send any reset commands to supplicant before shutting it down */
   2814             handleNetworkDisconnect();
   2815             if (mDhcpStateMachine != null) {
   2816                 mDhcpStateMachine.doQuit();
   2817             }
   2818 
   2819             if (DBG) log("stopping supplicant");
   2820             mWifiMonitor.stopSupplicant();
   2821 
   2822             /* Send ourselves a delayed message to indicate failure after a wait time */
   2823             sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
   2824                     ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
   2825             setWifiState(WIFI_STATE_DISABLING);
   2826             mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
   2827         }
   2828         @Override
   2829         public boolean processMessage(Message message) {
   2830             switch(message.what) {
   2831                 case WifiMonitor.SUP_CONNECTION_EVENT:
   2832                     loge("Supplicant connection received while stopping");
   2833                     break;
   2834                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
   2835                     if (DBG) log("Supplicant connection lost");
   2836                     handleSupplicantConnectionLoss();
   2837                     transitionTo(mInitialState);
   2838                     break;
   2839                 case CMD_STOP_SUPPLICANT_FAILED:
   2840                     if (message.arg1 == mSupplicantStopFailureToken) {
   2841                         loge("Timed out on a supplicant stop, kill and proceed");
   2842                         handleSupplicantConnectionLoss();
   2843                         transitionTo(mInitialState);
   2844                     }
   2845                     break;
   2846                 case CMD_START_SUPPLICANT:
   2847                 case CMD_STOP_SUPPLICANT:
   2848                 case CMD_START_AP:
   2849                 case CMD_STOP_AP:
   2850                 case CMD_START_DRIVER:
   2851                 case CMD_STOP_DRIVER:
   2852                 case CMD_SET_OPERATIONAL_MODE:
   2853                 case CMD_SET_COUNTRY_CODE:
   2854                 case CMD_SET_FREQUENCY_BAND:
   2855                 case CMD_START_PACKET_FILTERING:
   2856                 case CMD_STOP_PACKET_FILTERING:
   2857                     deferMessage(message);
   2858                     break;
   2859                 default:
   2860                     return NOT_HANDLED;
   2861             }
   2862             return HANDLED;
   2863         }
   2864     }
   2865 
   2866     class DriverStartingState extends State {
   2867         private int mTries;
   2868         @Override
   2869         public void enter() {
   2870             mTries = 1;
   2871             /* Send ourselves a delayed message to start driver a second time */
   2872             sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
   2873                         ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
   2874         }
   2875         @Override
   2876         public boolean processMessage(Message message) {
   2877             switch(message.what) {
   2878                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
   2879                     SupplicantState state = handleSupplicantStateChange(message);
   2880                     /* If suplicant is exiting out of INTERFACE_DISABLED state into
   2881                      * a state that indicates driver has started, it is ready to
   2882                      * receive driver commands
   2883                      */
   2884                     if (SupplicantState.isDriverActive(state)) {
   2885                         transitionTo(mDriverStartedState);
   2886                     }
   2887                     break;
   2888                 case CMD_DRIVER_START_TIMED_OUT:
   2889                     if (message.arg1 == mDriverStartToken) {
   2890                         if (mTries >= 2) {
   2891                             loge("Failed to start driver after " + mTries);
   2892                             transitionTo(mDriverStoppedState);
   2893                         } else {
   2894                             loge("Driver start failed, retrying");
   2895                             mWakeLock.acquire();
   2896                             mWifiNative.startDriver();
   2897                             mWakeLock.release();
   2898 
   2899                             ++mTries;
   2900                             /* Send ourselves a delayed message to start driver again */
   2901                             sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
   2902                                         ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
   2903                         }
   2904                     }
   2905                     break;
   2906                     /* Queue driver commands & connection events */
   2907                 case CMD_START_DRIVER:
   2908                 case CMD_STOP_DRIVER:
   2909                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
   2910                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
   2911                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
   2912                 case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
   2913                 case WifiMonitor.WPS_OVERLAP_EVENT:
   2914                 case CMD_SET_COUNTRY_CODE:
   2915                 case CMD_SET_FREQUENCY_BAND:
   2916                 case CMD_START_PACKET_FILTERING:
   2917                 case CMD_STOP_PACKET_FILTERING:
   2918                 case CMD_START_SCAN:
   2919                 case CMD_DISCONNECT:
   2920                 case CMD_REASSOCIATE:
   2921                 case CMD_RECONNECT:
   2922                     deferMessage(message);
   2923                     break;
   2924                 default:
   2925                     return NOT_HANDLED;
   2926             }
   2927             return HANDLED;
   2928         }
   2929     }
   2930 
   2931     class DriverStartedState extends State {
   2932         @Override
   2933         public void enter() {
   2934             mIsRunning = true;
   2935             mInDelayedStop = false;
   2936             mDelayedStopCounter++;
   2937             updateBatteryWorkSource(null);
   2938             /**
   2939              * Enable bluetooth coexistence scan mode when bluetooth connection is active.
   2940              * When this mode is on, some of the low-level scan parameters used by the
   2941              * driver are changed to reduce interference with bluetooth
   2942              */
   2943             mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
   2944             /* set country code */
   2945             setCountryCode();
   2946             /* set frequency band of operation */
   2947             setFrequencyBand();
   2948             /* initialize network state */
   2949             setNetworkDetailedState(DetailedState.DISCONNECTED);
   2950 
   2951             /* Remove any filtering on Multicast v6 at start */
   2952             mWifiNative.stopFilteringMulticastV6Packets();
   2953 
   2954             /* Reset Multicast v4 filtering state */
   2955             if (mFilteringMulticastV4Packets.get()) {
   2956                 mWifiNative.startFilteringMulticastV4Packets();
   2957             } else {
   2958                 mWifiNative.stopFilteringMulticastV4Packets();
   2959             }
   2960 
   2961             mDhcpActive = false;
   2962 
   2963             if (mBatchedScanSettings != null) {
   2964                 startBatchedScan();
   2965             }
   2966 
   2967             if (mOperationalMode != CONNECT_MODE) {
   2968                 mWifiNative.disconnect();
   2969                 mWifiConfigStore.disableAllNetworks();
   2970                 if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
   2971                     setWifiState(WIFI_STATE_DISABLED);
   2972                 }
   2973                 transitionTo(mScanModeState);
   2974             } else {
   2975                 /* Driver stop may have disabled networks, enable right after start */
   2976                 mWifiConfigStore.enableAllNetworks();
   2977 
   2978                 if (DBG) log("Attempting to reconnect to wifi network ..");
   2979                 mWifiNative.reconnect();
   2980 
   2981                 // Status pulls in the current supplicant state and network connection state
   2982                 // events over the monitor connection. This helps framework sync up with
   2983                 // current supplicant state
   2984                 mWifiNative.status();
   2985                 transitionTo(mDisconnectedState);
   2986             }
   2987 
   2988             // We may have missed screen update at boot
   2989             if (mScreenBroadcastReceived.get() == false) {
   2990                 PowerManager powerManager = (PowerManager)mContext.getSystemService(
   2991                         Context.POWER_SERVICE);
   2992                 handleScreenStateChanged(powerManager.isScreenOn());
   2993             } else {
   2994                 // Set the right suspend mode settings
   2995                 mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
   2996                         && mUserWantsSuspendOpt.get());
   2997             }
   2998             mWifiNative.setPowerSave(true);
   2999 
   3000             if (mP2pSupported) {
   3001                 if (mOperationalMode == CONNECT_MODE) {
   3002                     mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
   3003                 } else {
   3004                     // P2P statemachine starts in disabled state, and is not enabled until
   3005                     // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
   3006                     // keep it disabled.
   3007                 }
   3008             }
   3009 
   3010             final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
   3011             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   3012             intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
   3013             mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   3014         }
   3015 
   3016         @Override
   3017         public boolean processMessage(Message message) {
   3018             switch(message.what) {
   3019                 case CMD_START_SCAN:
   3020                     noteScanStart(message.arg1, (WorkSource) message.obj);
   3021                     startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
   3022                     break;
   3023                 case CMD_SET_BATCHED_SCAN:
   3024                     recordBatchedScanSettings((BatchedScanSettings)message.obj);
   3025                     startBatchedScan();
   3026                     break;
   3027                 case CMD_SET_COUNTRY_CODE:
   3028                     String country = (String) message.obj;
   3029                     if (DBG) log("set country code " + country);
   3030                     if (country != null) {
   3031                         country = country.toUpperCase(Locale.ROOT);
   3032                         if (mLastSetCountryCode == null
   3033                                 || country.equals(mLastSetCountryCode) == false) {
   3034                             if (mWifiNative.setCountryCode(country)) {
   3035                                 mLastSetCountryCode = country;
   3036                             } else {
   3037                                 loge("Failed to set country code " + country);
   3038                             }
   3039                         }
   3040                     }
   3041                     break;
   3042                 case CMD_SET_FREQUENCY_BAND:
   3043                     int band =  message.arg1;
   3044                     if (DBG) log("set frequency band " + band);
   3045                     if (mWifiNative.setBand(band)) {
   3046                         mFrequencyBand.set(band);
   3047                         // flush old data - like scan results
   3048                         mWifiNative.bssFlush();
   3049                         //Fetch the latest scan results when frequency band is set
   3050                         startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
   3051                     } else {
   3052                         loge("Failed to set frequency band " + band);
   3053                     }
   3054                     break;
   3055                 case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
   3056                     mBluetoothConnectionActive = (message.arg1 !=
   3057                             BluetoothAdapter.STATE_DISCONNECTED);
   3058                     mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
   3059                     break;
   3060                 case CMD_STOP_DRIVER:
   3061                     int mode = message.arg1;
   3062 
   3063                     /* Already doing a delayed stop */
   3064                     if (mInDelayedStop) {
   3065                         if (DBG) log("Already in delayed stop");
   3066                         break;
   3067                     }
   3068                     /* disconnect right now, but leave the driver running for a bit */
   3069                     mWifiConfigStore.disableAllNetworks();
   3070 
   3071                     mInDelayedStop = true;
   3072                     mDelayedStopCounter++;
   3073                     if (DBG) log("Delayed stop message " + mDelayedStopCounter);
   3074 
   3075                     /* send regular delayed shut down */
   3076                     Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
   3077                     driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
   3078                     mDriverStopIntent = PendingIntent.getBroadcast(mContext,
   3079                             DRIVER_STOP_REQUEST, driverStopIntent,
   3080                             PendingIntent.FLAG_UPDATE_CURRENT);
   3081 
   3082                     mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
   3083                             + mDriverStopDelayMs, mDriverStopIntent);
   3084                     break;
   3085                 case CMD_START_DRIVER:
   3086                     if (mInDelayedStop) {
   3087                         mInDelayedStop = false;
   3088                         mDelayedStopCounter++;
   3089                         mAlarmManager.cancel(mDriverStopIntent);
   3090                         if (DBG) log("Delayed stop ignored due to start");
   3091                         if (mOperationalMode == CONNECT_MODE) {
   3092                             mWifiConfigStore.enableAllNetworks();
   3093                         }
   3094                     }
   3095                     break;
   3096                 case CMD_DELAYED_STOP_DRIVER:
   3097                     if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
   3098                     if (message.arg1 != mDelayedStopCounter) break;
   3099                     if (getCurrentState() != mDisconnectedState) {
   3100                         mWifiNative.disconnect();
   3101                         handleNetworkDisconnect();
   3102                     }
   3103                     mWakeLock.acquire();
   3104                     mWifiNative.stopDriver();
   3105                     mWakeLock.release();
   3106                     if (mP2pSupported) {
   3107                         transitionTo(mWaitForP2pDisableState);
   3108                     } else {
   3109                         transitionTo(mDriverStoppingState);
   3110                     }
   3111                     break;
   3112                 case CMD_START_PACKET_FILTERING:
   3113                     if (message.arg1 == MULTICAST_V6) {
   3114                         mWifiNative.startFilteringMulticastV6Packets();
   3115                     } else if (message.arg1 == MULTICAST_V4) {
   3116                         mWifiNative.startFilteringMulticastV4Packets();
   3117                     } else {
   3118                         loge("Illegal arugments to CMD_START_PACKET_FILTERING");
   3119                     }
   3120                     break;
   3121                 case CMD_STOP_PACKET_FILTERING:
   3122                     if (message.arg1 == MULTICAST_V6) {
   3123                         mWifiNative.stopFilteringMulticastV6Packets();
   3124                     } else if (message.arg1 == MULTICAST_V4) {
   3125                         mWifiNative.stopFilteringMulticastV4Packets();
   3126                     } else {
   3127                         loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
   3128                     }
   3129                     break;
   3130                 case CMD_SET_SUSPEND_OPT_ENABLED:
   3131                     if (message.arg1 == 1) {
   3132                         setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
   3133                         mSuspendWakeLock.release();
   3134                     } else {
   3135                         setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
   3136                     }
   3137                     break;
   3138                 case CMD_SET_HIGH_PERF_MODE:
   3139                     if (message.arg1 == 1) {
   3140                         setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
   3141                     } else {
   3142                         setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
   3143                     }
   3144                     break;
   3145                 case CMD_ENABLE_TDLS:
   3146                     if (message.obj != null) {
   3147                         String remoteAddress = (String) message.obj;
   3148                         boolean enable = (message.arg1 == 1);
   3149                         mWifiNative.startTdls(remoteAddress, enable);
   3150                     }
   3151                     break;
   3152                 default:
   3153                     return NOT_HANDLED;
   3154             }
   3155             return HANDLED;
   3156         }
   3157         @Override
   3158         public void exit() {
   3159             mIsRunning = false;
   3160             updateBatteryWorkSource(null);
   3161             mScanResults = new ArrayList<ScanResult>();
   3162 
   3163             if (mBatchedScanSettings != null) {
   3164                 stopBatchedScan();
   3165             }
   3166 
   3167             final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
   3168             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   3169             intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
   3170             mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   3171             noteScanEnd(); // wrap up any pending request.
   3172 
   3173             mLastSetCountryCode = null;
   3174         }
   3175     }
   3176 
   3177     class WaitForP2pDisableState extends State {
   3178         private State mTransitionToState;
   3179         @Override
   3180         public void enter() {
   3181             switch (getCurrentMessage().what) {
   3182                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
   3183                     mTransitionToState = mInitialState;
   3184                     break;
   3185                 case CMD_DELAYED_STOP_DRIVER:
   3186                     mTransitionToState = mDriverStoppingState;
   3187                     break;
   3188                 case CMD_STOP_SUPPLICANT:
   3189                     mTransitionToState = mSupplicantStoppingState;
   3190                     break;
   3191                 default:
   3192                     mTransitionToState = mDriverStoppingState;
   3193                     break;
   3194             }
   3195             mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
   3196         }
   3197         @Override
   3198         public boolean processMessage(Message message) {
   3199             switch(message.what) {
   3200                 case WifiStateMachine.CMD_DISABLE_P2P_RSP:
   3201                     transitionTo(mTransitionToState);
   3202                     break;
   3203                 /* Defer wifi start/shut and driver commands */
   3204                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
   3205                 case CMD_START_SUPPLICANT:
   3206                 case CMD_STOP_SUPPLICANT:
   3207                 case CMD_START_AP:
   3208                 case CMD_STOP_AP:
   3209                 case CMD_START_DRIVER:
   3210                 case CMD_STOP_DRIVER:
   3211                 case CMD_SET_OPERATIONAL_MODE:
   3212                 case CMD_SET_COUNTRY_CODE:
   3213                 case CMD_SET_FREQUENCY_BAND:
   3214                 case CMD_START_PACKET_FILTERING:
   3215                 case CMD_STOP_PACKET_FILTERING:
   3216                 case CMD_START_SCAN:
   3217                 case CMD_DISCONNECT:
   3218                 case CMD_REASSOCIATE:
   3219                 case CMD_RECONNECT:
   3220                     deferMessage(message);
   3221                     break;
   3222                 default:
   3223                     return NOT_HANDLED;
   3224             }
   3225             return HANDLED;
   3226         }
   3227     }
   3228 
   3229     class DriverStoppingState extends State {
   3230         @Override
   3231         public boolean processMessage(Message message) {
   3232             switch(message.what) {
   3233                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
   3234                     SupplicantState state = handleSupplicantStateChange(message);
   3235                     if (state == SupplicantState.INTERFACE_DISABLED) {
   3236                         transitionTo(mDriverStoppedState);
   3237                     }
   3238                     break;
   3239                     /* Queue driver commands */
   3240                 case CMD_START_DRIVER:
   3241                 case CMD_STOP_DRIVER:
   3242                 case CMD_SET_COUNTRY_CODE:
   3243                 case CMD_SET_FREQUENCY_BAND:
   3244                 case CMD_START_PACKET_FILTERING:
   3245                 case CMD_STOP_PACKET_FILTERING:
   3246                 case CMD_START_SCAN:
   3247                 case CMD_DISCONNECT:
   3248                 case CMD_REASSOCIATE:
   3249                 case CMD_RECONNECT:
   3250                     deferMessage(message);
   3251                     break;
   3252                 default:
   3253                     return NOT_HANDLED;
   3254             }
   3255             return HANDLED;
   3256         }
   3257     }
   3258 
   3259     class DriverStoppedState extends State {
   3260         @Override
   3261         public boolean processMessage(Message message) {
   3262             switch (message.what) {
   3263                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
   3264                     StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
   3265                     SupplicantState state = stateChangeResult.state;
   3266                     // A WEXT bug means that we can be back to driver started state
   3267                     // unexpectedly
   3268                     if (SupplicantState.isDriverActive(state)) {
   3269                         transitionTo(mDriverStartedState);
   3270                     }
   3271                     break;
   3272                 case CMD_START_DRIVER:
   3273                     mWakeLock.acquire();
   3274                     mWifiNative.startDriver();
   3275                     mWakeLock.release();
   3276                     transitionTo(mDriverStartingState);
   3277                     break;
   3278                 default:
   3279                     return NOT_HANDLED;
   3280             }
   3281             return HANDLED;
   3282         }
   3283     }
   3284 
   3285     class ScanModeState extends State {
   3286         private int mLastOperationMode;
   3287         @Override
   3288         public void enter() {
   3289             mLastOperationMode = mOperationalMode;
   3290         }
   3291         @Override
   3292         public boolean processMessage(Message message) {
   3293             switch(message.what) {
   3294                 case CMD_SET_OPERATIONAL_MODE:
   3295                     if (message.arg1 == CONNECT_MODE) {
   3296 
   3297                         if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
   3298                             setWifiState(WIFI_STATE_ENABLED);
   3299                             // Load and re-enable networks when going back to enabled state
   3300                             // This is essential for networks to show up after restore
   3301                             mWifiConfigStore.loadAndEnableAllNetworks();
   3302                             mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
   3303                         } else {
   3304                             mWifiConfigStore.enableAllNetworks();
   3305                         }
   3306 
   3307                         mWifiNative.reconnect();
   3308 
   3309                         mOperationalMode = CONNECT_MODE;
   3310                         transitionTo(mDisconnectedState);
   3311                     } else {
   3312                         // Nothing to do
   3313                         return HANDLED;
   3314                     }
   3315                     break;
   3316                 // Handle scan. All the connection related commands are
   3317                 // handled only in ConnectModeState
   3318                 case CMD_START_SCAN:
   3319                     noteScanStart(message.arg1, (WorkSource) message.obj);
   3320                     startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
   3321                     break;
   3322                 default:
   3323                     return NOT_HANDLED;
   3324             }
   3325             return HANDLED;
   3326         }
   3327     }
   3328 
   3329     class ConnectModeState extends State {
   3330         @Override
   3331         public boolean processMessage(Message message) {
   3332             WifiConfiguration config;
   3333             boolean ok;
   3334             switch(message.what) {
   3335                 case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
   3336                     mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
   3337                     break;
   3338                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
   3339                     mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
   3340                     break;
   3341                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
   3342                     SupplicantState state = handleSupplicantStateChange(message);
   3343                     // A driver/firmware hang can now put the interface in a down state.
   3344                     // We detect the interface going down and recover from it
   3345                     if (!SupplicantState.isDriverActive(state)) {
   3346                         if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
   3347                             handleNetworkDisconnect();
   3348                         }
   3349                         log("Detected an interface down, restart driver");
   3350                         transitionTo(mDriverStoppedState);
   3351                         sendMessage(CMD_START_DRIVER);
   3352                         break;
   3353                     }
   3354 
   3355                     // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
   3356                     // when authentication times out after a successful connection,
   3357                     // we can figure this from the supplicant state. If supplicant
   3358                     // state is DISCONNECTED, but the mNetworkInfo says we are not
   3359                     // disconnected, we need to handle a disconnection
   3360                     if (state == SupplicantState.DISCONNECTED &&
   3361                             mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
   3362                         if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
   3363                         handleNetworkDisconnect();
   3364                         transitionTo(mDisconnectedState);
   3365                     }
   3366                     break;
   3367                 case WifiP2pService.DISCONNECT_WIFI_REQUEST:
   3368                     if (message.arg1 == 1) {
   3369                         mWifiNative.disconnect();
   3370                         mTemporarilyDisconnectWifi = true;
   3371                     } else {
   3372                         mWifiNative.reconnect();
   3373                         mTemporarilyDisconnectWifi = false;
   3374                     }
   3375                     break;
   3376                 case CMD_ADD_OR_UPDATE_NETWORK:
   3377                     config = (WifiConfiguration) message.obj;
   3378                     replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
   3379                             mWifiConfigStore.addOrUpdateNetwork(config));
   3380                     break;
   3381                 case CMD_REMOVE_NETWORK:
   3382                     ok = mWifiConfigStore.removeNetwork(message.arg1);
   3383                     replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
   3384                     break;
   3385                 case CMD_ENABLE_NETWORK:
   3386                     ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
   3387                     replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
   3388                     break;
   3389                 case CMD_ENABLE_ALL_NETWORKS:
   3390                     long time =  android.os.SystemClock.elapsedRealtime();
   3391                     if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
   3392                         mWifiConfigStore.enableAllNetworks();
   3393                         mLastEnableAllNetworksTime = time;
   3394                     }
   3395                     break;
   3396                 case WifiManager.DISABLE_NETWORK:
   3397                     if (mWifiConfigStore.disableNetwork(message.arg1,
   3398                             WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
   3399                         replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
   3400                     } else {
   3401                         replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
   3402                                 WifiManager.ERROR);
   3403                     }
   3404                     break;
   3405                 case CMD_BLACKLIST_NETWORK:
   3406                     mWifiNative.addToBlacklist((String)message.obj);
   3407                     break;
   3408                 case CMD_CLEAR_BLACKLIST:
   3409                     mWifiNative.clearBlacklist();
   3410                     break;
   3411                 case CMD_SAVE_CONFIG:
   3412                     ok = mWifiConfigStore.saveConfig();
   3413                     replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
   3414 
   3415                     // Inform the backup manager about a data change
   3416                     IBackupManager ibm = IBackupManager.Stub.asInterface(
   3417                             ServiceManager.getService(Context.BACKUP_SERVICE));
   3418                     if (ibm != null) {
   3419                         try {
   3420                             ibm.dataChanged("com.android.providers.settings");
   3421                         } catch (Exception e) {
   3422                             // Try again later
   3423                         }
   3424                     }
   3425                     break;
   3426                 case CMD_GET_CONFIGURED_NETWORKS:
   3427                     replyToMessage(message, message.what,
   3428                             mWifiConfigStore.getConfiguredNetworks());
   3429                     break;
   3430                     /* Do a redundant disconnect without transition */
   3431                 case CMD_DISCONNECT:
   3432                     mWifiNative.disconnect();
   3433                     break;
   3434                 case CMD_RECONNECT:
   3435                     mWifiNative.reconnect();
   3436                     break;
   3437                 case CMD_REASSOCIATE:
   3438                     mWifiNative.reassociate();
   3439                     break;
   3440                 case CMD_RELOAD_TLS_AND_RECONNECT:
   3441                     if (mWifiConfigStore.needsUnlockedKeyStore()) {
   3442                         logd("Reconnecting to give a chance to un-connected TLS networks");
   3443                         mWifiNative.disconnect();
   3444                         mWifiNative.reconnect();
   3445                     }
   3446                     break;
   3447                 case WifiManager.CONNECT_NETWORK:
   3448                     /* The connect message can contain a network id passed as arg1 on message or
   3449                      * or a config passed as obj on message.
   3450                      * For a new network, a config is passed to create and connect.
   3451                      * For an existing network, a network id is passed
   3452                      */
   3453                     int netId = message.arg1;
   3454                     config = (WifiConfiguration) message.obj;
   3455 
   3456                     /* Save the network config */
   3457                     if (config != null) {
   3458                         NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
   3459                         netId = result.getNetworkId();
   3460                     }
   3461 
   3462                     if (mWifiConfigStore.selectNetwork(netId) &&
   3463                             mWifiNative.reconnect()) {
   3464                         /* The state tracker handles enabling networks upon completion/failure */
   3465                         mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
   3466                         replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
   3467                         /* Expect a disconnection from the old connection */
   3468                         transitionTo(mDisconnectingState);
   3469                     } else {
   3470                         loge("Failed to connect config: " + config + " netId: " + netId);
   3471                         replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
   3472                                 WifiManager.ERROR);
   3473                         break;
   3474                     }
   3475                     break;
   3476                 case WifiManager.SAVE_NETWORK:
   3477                     config = (WifiConfiguration) message.obj;
   3478                     NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
   3479                     if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
   3480                         replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
   3481                     } else {
   3482                         loge("Failed to save network");
   3483                         replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
   3484                                 WifiManager.ERROR);
   3485                     }
   3486                     break;
   3487                 case WifiManager.FORGET_NETWORK:
   3488                     if (mWifiConfigStore.forgetNetwork(message.arg1)) {
   3489                         replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
   3490                     } else {
   3491                         loge("Failed to forget network");
   3492                         replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
   3493                                 WifiManager.ERROR);
   3494                     }
   3495                     break;
   3496                 case WifiManager.START_WPS:
   3497                     WpsInfo wpsInfo = (WpsInfo) message.obj;
   3498                     WpsResult wpsResult;
   3499                     switch (wpsInfo.setup) {
   3500                         case WpsInfo.PBC:
   3501                             wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
   3502                             break;
   3503                         case WpsInfo.KEYPAD:
   3504                             wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
   3505                             break;
   3506                         case WpsInfo.DISPLAY:
   3507                             wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
   3508                             break;
   3509                         default:
   3510                             wpsResult = new WpsResult(Status.FAILURE);
   3511                             loge("Invalid setup for WPS");
   3512                             break;
   3513                     }
   3514                     if (wpsResult.status == Status.SUCCESS) {
   3515                         replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
   3516                         transitionTo(mWpsRunningState);
   3517                     } else {
   3518                         loge("Failed to start WPS with config " + wpsInfo.toString());
   3519                         replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
   3520                     }
   3521                     break;
   3522                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
   3523                     if (DBG) log("Network connection established");
   3524                     mLastNetworkId = message.arg1;
   3525                     mLastBssid = (String) message.obj;
   3526 
   3527                     mWifiInfo.setBSSID(mLastBssid);
   3528                     mWifiInfo.setNetworkId(mLastNetworkId);
   3529                     /* send event to CM & network change broadcast */
   3530                     setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
   3531                     sendNetworkStateChangeBroadcast(mLastBssid);
   3532                     transitionTo(mObtainingIpState);
   3533                     break;
   3534                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
   3535                     if (DBG) log("Network connection lost");
   3536                     handleNetworkDisconnect();
   3537                     transitionTo(mDisconnectedState);
   3538                     break;
   3539                 default:
   3540                     return NOT_HANDLED;
   3541             }
   3542             return HANDLED;
   3543         }
   3544     }
   3545 
   3546     class L2ConnectedState extends State {
   3547         @Override
   3548         public void enter() {
   3549             mRssiPollToken++;
   3550             if (mEnableRssiPolling) {
   3551                 sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
   3552             }
   3553         }
   3554 
   3555         @Override
   3556         public void exit() {
   3557             handleNetworkDisconnect();
   3558         }
   3559 
   3560         @Override
   3561         public boolean processMessage(Message message) {
   3562             switch (message.what) {
   3563               case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
   3564                   handlePreDhcpSetup();
   3565                   break;
   3566               case DhcpStateMachine.CMD_POST_DHCP_ACTION:
   3567                   handlePostDhcpSetup();
   3568                   if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
   3569                       if (DBG) log("DHCP successful");
   3570                       handleSuccessfulIpConfiguration((DhcpResults) message.obj);
   3571                       transitionTo(mVerifyingLinkState);
   3572                   } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
   3573                       if (DBG) log("DHCP failed");
   3574                       handleFailedIpConfiguration();
   3575                       transitionTo(mDisconnectingState);
   3576                   }
   3577                   break;
   3578                 case CMD_DISCONNECT:
   3579                     mWifiNative.disconnect();
   3580                     transitionTo(mDisconnectingState);
   3581                     break;
   3582                 case WifiP2pService.DISCONNECT_WIFI_REQUEST:
   3583                     if (message.arg1 == 1) {
   3584                         mWifiNative.disconnect();
   3585                         mTemporarilyDisconnectWifi = true;
   3586                         transitionTo(mDisconnectingState);
   3587                     }
   3588                     break;
   3589                 case CMD_SET_OPERATIONAL_MODE:
   3590                     if (message.arg1 != CONNECT_MODE) {
   3591                         sendMessage(CMD_DISCONNECT);
   3592                         deferMessage(message);
   3593                     }
   3594                     break;
   3595                 case CMD_START_SCAN:
   3596                     /* Do not attempt to connect when we are already connected */
   3597                     noteScanStart(message.arg1, (WorkSource) message.obj);
   3598                     startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
   3599                     break;
   3600                     /* Ignore connection to same network */
   3601                 case WifiManager.CONNECT_NETWORK:
   3602                     int netId = message.arg1;
   3603                     if (mWifiInfo.getNetworkId() == netId) {
   3604                         break;
   3605                     }
   3606                     return NOT_HANDLED;
   3607                 case WifiManager.SAVE_NETWORK:
   3608                     WifiConfiguration config = (WifiConfiguration) message.obj;
   3609                     NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
   3610                     if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
   3611                         if (result.hasIpChanged()) {
   3612                             log("Reconfiguring IP on connection");
   3613                             transitionTo(mObtainingIpState);
   3614                         }
   3615                         if (result.hasProxyChanged()) {
   3616                             log("Reconfiguring proxy on connection");
   3617                             updateLinkProperties();
   3618                         }
   3619                     }
   3620 
   3621                     if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
   3622                         replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
   3623                     } else {
   3624                         loge("Failed to save network");
   3625                         replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
   3626                                 WifiManager.ERROR);
   3627                     }
   3628                     break;
   3629                     /* Ignore */
   3630                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
   3631                     break;
   3632                 case CMD_RSSI_POLL:
   3633                     if (message.arg1 == mRssiPollToken) {
   3634                         // Get Info and continue polling
   3635                         fetchRssiAndLinkSpeedNative();
   3636                         sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
   3637                                 mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
   3638                     } else {
   3639                         // Polling has completed
   3640                     }
   3641                     break;
   3642                 case CMD_ENABLE_RSSI_POLL:
   3643                     mEnableRssiPolling = (message.arg1 == 1);
   3644                     mRssiPollToken++;
   3645                     if (mEnableRssiPolling) {
   3646                         // first poll
   3647                         fetchRssiAndLinkSpeedNative();
   3648                         sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
   3649                                 mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
   3650                     }
   3651                     break;
   3652                 case WifiManager.RSSI_PKTCNT_FETCH:
   3653                     RssiPacketCountInfo info = new RssiPacketCountInfo();
   3654                     fetchRssiAndLinkSpeedNative();
   3655                     info.rssi = mWifiInfo.getRssi();
   3656                     fetchPktcntNative(info);
   3657                     replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
   3658                     break;
   3659                 default:
   3660                     return NOT_HANDLED;
   3661             }
   3662 
   3663             return HANDLED;
   3664         }
   3665     }
   3666 
   3667     class ObtainingIpState extends State {
   3668         @Override
   3669         public void enter() {
   3670             if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
   3671                 // TODO: If we're switching between static IP configuration and DHCP, remove the
   3672                 // static configuration first.
   3673                 startDhcp();
   3674             } else {
   3675                 // stop any running dhcp before assigning static IP
   3676                 stopDhcp();
   3677                 DhcpResults dhcpResults = new DhcpResults(
   3678                         mWifiConfigStore.getLinkProperties(mLastNetworkId));
   3679                 InterfaceConfiguration ifcg = new InterfaceConfiguration();
   3680                 Iterator<LinkAddress> addrs =
   3681                         dhcpResults.linkProperties.getLinkAddresses().iterator();
   3682                 if (!addrs.hasNext()) {
   3683                     loge("Static IP lacks address");
   3684                     sendMessage(CMD_STATIC_IP_FAILURE);
   3685                 } else {
   3686                     ifcg.setLinkAddress(addrs.next());
   3687                     ifcg.setInterfaceUp();
   3688                     try {
   3689                         mNwService.setInterfaceConfig(mInterfaceName, ifcg);
   3690                         if (DBG) log("Static IP configuration succeeded");
   3691                         sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
   3692                     } catch (RemoteException re) {
   3693                         loge("Static IP configuration failed: " + re);
   3694                         sendMessage(CMD_STATIC_IP_FAILURE);
   3695                     } catch (IllegalStateException e) {
   3696                         loge("Static IP configuration failed: " + e);
   3697                         sendMessage(CMD_STATIC_IP_FAILURE);
   3698                     }
   3699                 }
   3700             }
   3701         }
   3702       @Override
   3703       public boolean processMessage(Message message) {
   3704           if (DBG) log(getName() + message.toString() + "\n");
   3705           switch(message.what) {
   3706             case CMD_STATIC_IP_SUCCESS:
   3707                   handleSuccessfulIpConfiguration((DhcpResults) message.obj);
   3708                   transitionTo(mVerifyingLinkState);
   3709                   break;
   3710               case CMD_STATIC_IP_FAILURE:
   3711                   handleFailedIpConfiguration();
   3712                   transitionTo(mDisconnectingState);
   3713                   break;
   3714              case WifiManager.SAVE_NETWORK:
   3715                   deferMessage(message);
   3716                   break;
   3717                   /* Defer any power mode changes since we must keep active power mode at DHCP */
   3718               case CMD_SET_HIGH_PERF_MODE:
   3719                   deferMessage(message);
   3720                   break;
   3721                   /* Defer scan request since we should not switch to other channels at DHCP */
   3722               case CMD_START_SCAN:
   3723                   deferMessage(message);
   3724                   break;
   3725               default:
   3726                   return NOT_HANDLED;
   3727           }
   3728           return HANDLED;
   3729       }
   3730     }
   3731 
   3732     class VerifyingLinkState extends State {
   3733         @Override
   3734         public void enter() {
   3735             log(getName() + " enter");
   3736             setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
   3737             mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
   3738             sendNetworkStateChangeBroadcast(mLastBssid);
   3739         }
   3740         @Override
   3741         public boolean processMessage(Message message) {
   3742             switch (message.what) {
   3743                 case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
   3744                     //stay here
   3745                     log(getName() + " POOR_LINK_DETECTED: no transition");
   3746                     break;
   3747                 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
   3748                     log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
   3749                     transitionTo(mCaptivePortalCheckState);
   3750                     break;
   3751                 default:
   3752                     if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
   3753                     return NOT_HANDLED;
   3754             }
   3755             return HANDLED;
   3756         }
   3757     }
   3758 
   3759     class CaptivePortalCheckState extends State {
   3760         @Override
   3761         public void enter() {
   3762             log(getName() + " enter");
   3763             setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
   3764             mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CAPTIVE_PORTAL_CHECK);
   3765             sendNetworkStateChangeBroadcast(mLastBssid);
   3766         }
   3767         @Override
   3768         public boolean processMessage(Message message) {
   3769             switch (message.what) {
   3770                 case CMD_CAPTIVE_CHECK_COMPLETE:
   3771                     log(getName() + " CMD_CAPTIVE_CHECK_COMPLETE");
   3772                     try {
   3773                         mNwService.enableIpv6(mInterfaceName);
   3774                     } catch (RemoteException re) {
   3775                         loge("Failed to enable IPv6: " + re);
   3776                     } catch (IllegalStateException e) {
   3777                         loge("Failed to enable IPv6: " + e);
   3778                     }
   3779                     setNetworkDetailedState(DetailedState.CONNECTED);
   3780                     mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
   3781                     sendNetworkStateChangeBroadcast(mLastBssid);
   3782                     transitionTo(mConnectedState);
   3783                     break;
   3784                 default:
   3785                     return NOT_HANDLED;
   3786             }
   3787             return HANDLED;
   3788         }
   3789     }
   3790 
   3791     class ConnectedState extends State {
   3792         @Override
   3793         public boolean processMessage(Message message) {
   3794             switch (message.what) {
   3795                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
   3796                     if (DBG) log("Watchdog reports poor link");
   3797                     try {
   3798                         mNwService.disableIpv6(mInterfaceName);
   3799                     } catch (RemoteException re) {
   3800                         loge("Failed to disable IPv6: " + re);
   3801                     } catch (IllegalStateException e) {
   3802                         loge("Failed to disable IPv6: " + e);
   3803                     }
   3804                     /* Report a disconnect */
   3805                     setNetworkDetailedState(DetailedState.DISCONNECTED);
   3806                     mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
   3807                     sendNetworkStateChangeBroadcast(mLastBssid);
   3808 
   3809                     transitionTo(mVerifyingLinkState);
   3810                     break;
   3811                 default:
   3812                     return NOT_HANDLED;
   3813             }
   3814             return HANDLED;
   3815         }
   3816         @Override
   3817         public void exit() {
   3818             /* Request a CS wakelock during transition to mobile */
   3819             checkAndSetConnectivityInstance();
   3820             mCm.requestNetworkTransitionWakelock(getName());
   3821         }
   3822     }
   3823 
   3824     class DisconnectingState extends State {
   3825         @Override
   3826         public boolean processMessage(Message message) {
   3827             switch (message.what) {
   3828                 case CMD_SET_OPERATIONAL_MODE:
   3829                     if (message.arg1 != CONNECT_MODE) {
   3830                         deferMessage(message);
   3831                     }
   3832                     break;
   3833                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
   3834                     /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
   3835                      * we have missed the network disconnection, transition to mDisconnectedState
   3836                      * and handle the rest of the events there
   3837                      */
   3838                     deferMessage(message);
   3839                     handleNetworkDisconnect();
   3840                     transitionTo(mDisconnectedState);
   3841                     break;
   3842                 default:
   3843                     return NOT_HANDLED;
   3844             }
   3845             return HANDLED;
   3846         }
   3847     }
   3848 
   3849     class DisconnectedState extends State {
   3850         private boolean mAlarmEnabled = false;
   3851         /* This is set from the overlay config file or from a secure setting.
   3852          * A value of 0 disables scanning in the framework.
   3853          */
   3854         private long mFrameworkScanIntervalMs;
   3855 
   3856         private void setScanAlarm(boolean enabled) {
   3857             if (enabled == mAlarmEnabled) return;
   3858             if (enabled) {
   3859                 if (mFrameworkScanIntervalMs > 0) {
   3860                     mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
   3861                             System.currentTimeMillis() + mFrameworkScanIntervalMs,
   3862                             mFrameworkScanIntervalMs,
   3863                             mScanIntent);
   3864                     mAlarmEnabled = true;
   3865                 }
   3866             } else {
   3867                 mAlarmManager.cancel(mScanIntent);
   3868                 mAlarmEnabled = false;
   3869             }
   3870         }
   3871 
   3872         @Override
   3873         public void enter() {
   3874             // We dont scan frequently if this is a temporary disconnect
   3875             // due to p2p
   3876             if (mTemporarilyDisconnectWifi) {
   3877                 mWifiP2pChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_RESPONSE);
   3878                 return;
   3879             }
   3880 
   3881             mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
   3882                     Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
   3883                     mDefaultFrameworkScanIntervalMs);
   3884             /*
   3885              * We initiate background scanning if it is enabled, otherwise we
   3886              * initiate an infrequent scan that wakes up the device to ensure
   3887              * a user connects to an access point on the move
   3888              */
   3889             if (mEnableBackgroundScan) {
   3890                 /* If a regular scan result is pending, do not initiate background
   3891                  * scan until the scan results are returned. This is needed because
   3892                  * initiating a background scan will cancel the regular scan and
   3893                  * scan results will not be returned until background scanning is
   3894                  * cleared
   3895                  */
   3896                 if (!mScanResultIsPending) {
   3897                     mWifiNative.enableBackgroundScan(true);
   3898                 }
   3899             } else {
   3900                 setScanAlarm(true);
   3901             }
   3902 
   3903             /**
   3904              * If we have no networks saved, the supplicant stops doing the periodic scan.
   3905              * The scans are useful to notify the user of the presence of an open network.
   3906              * Note that these are not wake up scans.
   3907              */
   3908             if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
   3909                 sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
   3910                             ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
   3911             }
   3912         }
   3913         @Override
   3914         public boolean processMessage(Message message) {
   3915             boolean ret = HANDLED;
   3916             switch (message.what) {
   3917                 case CMD_NO_NETWORKS_PERIODIC_SCAN:
   3918                     if (mP2pConnected.get()) break;
   3919                     if (message.arg1 == mPeriodicScanToken &&
   3920                             mWifiConfigStore.getConfiguredNetworks().size() == 0) {
   3921                         sendMessage(CMD_START_SCAN, UNKNOWN_SCAN_SOURCE, 0, (WorkSource) null);
   3922                         sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
   3923                                     ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
   3924                     }
   3925                     break;
   3926                 case WifiManager.FORGET_NETWORK:
   3927                 case CMD_REMOVE_NETWORK:
   3928                     // Set up a delayed message here. After the forget/remove is handled
   3929                     // the handled delayed message will determine if there is a need to
   3930                     // scan and continue
   3931                     sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
   3932                                 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
   3933                     ret = NOT_HANDLED;
   3934                     break;
   3935                 case CMD_SET_OPERATIONAL_MODE:
   3936                     if (message.arg1 != CONNECT_MODE) {
   3937                         mOperationalMode = message.arg1;
   3938 
   3939                         mWifiConfigStore.disableAllNetworks();
   3940                         if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
   3941                             mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
   3942                             setWifiState(WIFI_STATE_DISABLED);
   3943                         }
   3944 
   3945                         transitionTo(mScanModeState);
   3946                     }
   3947                     break;
   3948                 case CMD_ENABLE_BACKGROUND_SCAN:
   3949                     mEnableBackgroundScan = (message.arg1 == 1);
   3950                     if (mEnableBackgroundScan) {
   3951                         mWifiNative.enableBackgroundScan(true);
   3952                         setScanAlarm(false);
   3953                     } else {
   3954                         mWifiNative.enableBackgroundScan(false);
   3955                         setScanAlarm(true);
   3956                     }
   3957                     break;
   3958                     /* Ignore network disconnect */
   3959                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
   3960                     break;
   3961                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
   3962                     StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
   3963                     setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
   3964                     /* ConnectModeState does the rest of the handling */
   3965                     ret = NOT_HANDLED;
   3966                     break;
   3967                 case CMD_START_SCAN:
   3968                     /* Disable background scan temporarily during a regular scan */
   3969                     if (mEnableBackgroundScan) {
   3970                         mWifiNative.enableBackgroundScan(false);
   3971                     }
   3972                     /* Handled in parent state */
   3973                     ret = NOT_HANDLED;
   3974                     break;
   3975                 case WifiMonitor.SCAN_RESULTS_EVENT:
   3976                     /* Re-enable background scan when a pending scan result is received */
   3977                     if (mEnableBackgroundScan && mScanResultIsPending) {
   3978                         mWifiNative.enableBackgroundScan(true);
   3979                     }
   3980                     /* Handled in parent state */
   3981                     ret = NOT_HANDLED;
   3982                     break;
   3983                 case WifiP2pService.P2P_CONNECTION_CHANGED:
   3984                     NetworkInfo info = (NetworkInfo) message.obj;
   3985                     mP2pConnected.set(info.isConnected());
   3986                     if (mP2pConnected.get()) {
   3987                         int defaultInterval = mContext.getResources().getInteger(
   3988                                 R.integer.config_wifi_scan_interval_p2p_connected);
   3989                         long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
   3990                                 Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
   3991                                 defaultInterval);
   3992                         mWifiNative.setScanInterval((int) scanIntervalMs/1000);
   3993                     } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
   3994                         if (DBG) log("Turn on scanning after p2p disconnected");
   3995                         sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
   3996                                     ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
   3997                     }
   3998                 case CMD_RECONNECT:
   3999                 case CMD_REASSOCIATE:
   4000                     if (mTemporarilyDisconnectWifi) {
   4001                         // Drop a third party reconnect/reassociate if STA is
   4002                         // temporarily disconnected for p2p
   4003                         break;
   4004                     } else {
   4005                         // ConnectModeState handles it
   4006                         ret = NOT_HANDLED;
   4007                     }
   4008                     break;
   4009                 default:
   4010                     ret = NOT_HANDLED;
   4011             }
   4012             return ret;
   4013         }
   4014 
   4015         @Override
   4016         public void exit() {
   4017             /* No need for a background scan upon exit from a disconnected state */
   4018             if (mEnableBackgroundScan) {
   4019                 mWifiNative.enableBackgroundScan(false);
   4020             }
   4021             setScanAlarm(false);
   4022         }
   4023     }
   4024 
   4025     class WpsRunningState extends State {
   4026         //Tracks the source to provide a reply
   4027         private Message mSourceMessage;
   4028         @Override
   4029         public void enter() {
   4030             mSourceMessage = Message.obtain(getCurrentMessage());
   4031         }
   4032         @Override
   4033         public boolean processMessage(Message message) {
   4034             switch (message.what) {
   4035                 case WifiMonitor.WPS_SUCCESS_EVENT:
   4036                     // Ignore intermediate success, wait for full connection
   4037                     break;
   4038                 case WifiMonitor.NETWORK_CONNECTION_EVENT:
   4039                     replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
   4040                     mSourceMessage.recycle();
   4041                     mSourceMessage = null;
   4042                     deferMessage(message);
   4043                     transitionTo(mDisconnectedState);
   4044                     break;
   4045                 case WifiMonitor.WPS_OVERLAP_EVENT:
   4046                     replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
   4047                             WifiManager.WPS_OVERLAP_ERROR);
   4048                     mSourceMessage.recycle();
   4049                     mSourceMessage = null;
   4050                     transitionTo(mDisconnectedState);
   4051                     break;
   4052                 case WifiMonitor.WPS_FAIL_EVENT:
   4053                     //arg1 has the reason for the failure
   4054                     replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
   4055                     mSourceMessage.recycle();
   4056                     mSourceMessage = null;
   4057                     transitionTo(mDisconnectedState);
   4058                     break;
   4059                 case WifiMonitor.WPS_TIMEOUT_EVENT:
   4060                     replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
   4061                             WifiManager.WPS_TIMED_OUT);
   4062                     mSourceMessage.recycle();
   4063                     mSourceMessage = null;
   4064                     transitionTo(mDisconnectedState);
   4065                     break;
   4066                 case WifiManager.START_WPS:
   4067                     replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
   4068                     break;
   4069                 case WifiManager.CANCEL_WPS:
   4070                     if (mWifiNative.cancelWps()) {
   4071                         replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
   4072                     } else {
   4073                         replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
   4074                     }
   4075                     transitionTo(mDisconnectedState);
   4076                     break;
   4077                 /* Defer all commands that can cause connections to a different network
   4078                  * or put the state machine out of connect mode
   4079                  */
   4080                 case CMD_STOP_DRIVER:
   4081                 case CMD_SET_OPERATIONAL_MODE:
   4082                 case WifiManager.CONNECT_NETWORK:
   4083                 case CMD_ENABLE_NETWORK:
   4084                 case CMD_RECONNECT:
   4085                 case CMD_REASSOCIATE:
   4086                     deferMessage(message);
   4087                     break;
   4088                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
   4089                     if (DBG) log("Network connection lost");
   4090                     handleNetworkDisconnect();
   4091                     break;
   4092                 case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
   4093                     if (DBG) log("Ignore Assoc reject event during WPS Connection");
   4094                     break;
   4095                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
   4096                     // Disregard auth failure events during WPS connection. The
   4097                     // EAP sequence is retried several times, and there might be
   4098                     // failures (especially for wps pin). We will get a WPS_XXX
   4099                     // event at the end of the sequence anyway.
   4100                     if (DBG) log("Ignore auth failure during WPS connection");
   4101                     break;
   4102                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
   4103                     //Throw away supplicant state changes when WPS is running.
   4104                     //We will start getting supplicant state changes once we get
   4105                     //a WPS success or failure
   4106                     break;
   4107                 default:
   4108                     return NOT_HANDLED;
   4109             }
   4110             return HANDLED;
   4111         }
   4112 
   4113         @Override
   4114         public void exit() {
   4115             mWifiConfigStore.enableAllNetworks();
   4116             mWifiConfigStore.loadConfiguredNetworks();
   4117         }
   4118     }
   4119 
   4120     class SoftApStartingState extends State {
   4121         @Override
   4122         public void enter() {
   4123             final Message message = getCurrentMessage();
   4124             if (message.what == CMD_START_AP) {
   4125                 final WifiConfiguration config = (WifiConfiguration) message.obj;
   4126 
   4127                 if (config == null) {
   4128                     mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
   4129                 } else {
   4130                     mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
   4131                     startSoftApWithConfig(config);
   4132                 }
   4133             } else {
   4134                 throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
   4135             }
   4136         }
   4137         @Override
   4138         public boolean processMessage(Message message) {
   4139             switch(message.what) {
   4140                 case CMD_START_SUPPLICANT:
   4141                 case CMD_STOP_SUPPLICANT:
   4142                 case CMD_START_AP:
   4143                 case CMD_STOP_AP:
   4144                 case CMD_START_DRIVER:
   4145                 case CMD_STOP_DRIVER:
   4146                 case CMD_SET_OPERATIONAL_MODE:
   4147                 case CMD_SET_COUNTRY_CODE:
   4148                 case CMD_SET_FREQUENCY_BAND:
   4149                 case CMD_START_PACKET_FILTERING:
   4150                 case CMD_STOP_PACKET_FILTERING:
   4151                 case CMD_TETHER_STATE_CHANGE:
   4152                     deferMessage(message);
   4153                     break;
   4154                 case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
   4155                     WifiConfiguration config = (WifiConfiguration) message.obj;
   4156                     if (config != null) {
   4157                         startSoftApWithConfig(config);
   4158                     } else {
   4159                         loge("Softap config is null!");
   4160                         sendMessage(CMD_START_AP_FAILURE);
   4161                     }
   4162                     break;
   4163                 case CMD_START_AP_SUCCESS:
   4164                     setWifiApState(WIFI_AP_STATE_ENABLED);
   4165                     transitionTo(mSoftApStartedState);
   4166                     break;
   4167                 case CMD_START_AP_FAILURE:
   4168                     setWifiApState(WIFI_AP_STATE_FAILED);
   4169                     transitionTo(mInitialState);
   4170                     break;
   4171                 default:
   4172                     return NOT_HANDLED;
   4173             }
   4174             return HANDLED;
   4175         }
   4176     }
   4177 
   4178     class SoftApStartedState extends State {
   4179         @Override
   4180         public boolean processMessage(Message message) {
   4181             switch(message.what) {
   4182                 case CMD_STOP_AP:
   4183                     if (DBG) log("Stopping Soft AP");
   4184                     /* We have not tethered at this point, so we just shutdown soft Ap */
   4185                     try {
   4186                         mNwService.stopAccessPoint(mInterfaceName);
   4187                     } catch(Exception e) {
   4188                         loge("Exception in stopAccessPoint()");
   4189                     }
   4190                     setWifiApState(WIFI_AP_STATE_DISABLED);
   4191                     transitionTo(mInitialState);
   4192                     break;
   4193                 case CMD_START_AP:
   4194                     // Ignore a start on a running access point
   4195                     break;
   4196                     /* Fail client mode operation when soft AP is enabled */
   4197                 case CMD_START_SUPPLICANT:
   4198                     loge("Cannot start supplicant with a running soft AP");
   4199                     setWifiState(WIFI_STATE_UNKNOWN);
   4200                     break;
   4201                 case CMD_TETHER_STATE_CHANGE:
   4202                     TetherStateChange stateChange = (TetherStateChange) message.obj;
   4203                     if (startTethering(stateChange.available)) {
   4204                         transitionTo(mTetheringState);
   4205                     }
   4206                     break;
   4207                 default:
   4208                     return NOT_HANDLED;
   4209             }
   4210             return HANDLED;
   4211         }
   4212     }
   4213 
   4214     class TetheringState extends State {
   4215         @Override
   4216         public void enter() {
   4217             /* Send ourselves a delayed message to shut down if tethering fails to notify */
   4218             sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
   4219                     ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
   4220         }
   4221         @Override
   4222         public boolean processMessage(Message message) {
   4223             switch(message.what) {
   4224                 case CMD_TETHER_STATE_CHANGE:
   4225                     TetherStateChange stateChange = (TetherStateChange) message.obj;
   4226                     if (isWifiTethered(stateChange.active)) {
   4227                         transitionTo(mTetheredState);
   4228                     }
   4229                     return HANDLED;
   4230                 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
   4231                     if (message.arg1 == mTetherToken) {
   4232                         loge("Failed to get tether update, shutdown soft access point");
   4233                         transitionTo(mSoftApStartedState);
   4234                         // Needs to be first thing handled
   4235                         sendMessageAtFrontOfQueue(CMD_STOP_AP);
   4236                     }
   4237                     break;
   4238                 case CMD_START_SUPPLICANT:
   4239                 case CMD_STOP_SUPPLICANT:
   4240                 case CMD_START_AP:
   4241                 case CMD_STOP_AP:
   4242                 case CMD_START_DRIVER:
   4243                 case CMD_STOP_DRIVER:
   4244                 case CMD_SET_OPERATIONAL_MODE:
   4245                 case CMD_SET_COUNTRY_CODE:
   4246                 case CMD_SET_FREQUENCY_BAND:
   4247                 case CMD_START_PACKET_FILTERING:
   4248                 case CMD_STOP_PACKET_FILTERING:
   4249                     deferMessage(message);
   4250                     break;
   4251                 default:
   4252                     return NOT_HANDLED;
   4253             }
   4254             return HANDLED;
   4255         }
   4256     }
   4257 
   4258     class TetheredState extends State {
   4259         @Override
   4260         public boolean processMessage(Message message) {
   4261             switch(message.what) {
   4262                 case CMD_TETHER_STATE_CHANGE:
   4263                     TetherStateChange stateChange = (TetherStateChange) message.obj;
   4264                     if (!isWifiTethered(stateChange.active)) {
   4265                         loge("Tethering reports wifi as untethered!, shut down soft Ap");
   4266                         setHostApRunning(null, false);
   4267                         setHostApRunning(null, true);
   4268                     }
   4269                     return HANDLED;
   4270                 case CMD_STOP_AP:
   4271                     if (DBG) log("Untethering before stopping AP");
   4272                     setWifiApState(WIFI_AP_STATE_DISABLING);
   4273                     stopTethering();
   4274                     transitionTo(mUntetheringState);
   4275                     // More work to do after untethering
   4276                     deferMessage(message);
   4277                     break;
   4278                 default:
   4279                     return NOT_HANDLED;
   4280             }
   4281             return HANDLED;
   4282         }
   4283     }
   4284 
   4285     class UntetheringState extends State {
   4286         @Override
   4287         public void enter() {
   4288             /* Send ourselves a delayed message to shut down if tethering fails to notify */
   4289             sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
   4290                     ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
   4291 
   4292         }
   4293         @Override
   4294         public boolean processMessage(Message message) {
   4295             switch(message.what) {
   4296                 case CMD_TETHER_STATE_CHANGE:
   4297                     TetherStateChange stateChange = (TetherStateChange) message.obj;
   4298 
   4299                     /* Wait till wifi is untethered */
   4300                     if (isWifiTethered(stateChange.active)) break;
   4301 
   4302                     transitionTo(mSoftApStartedState);
   4303                     break;
   4304                 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
   4305                     if (message.arg1 == mTetherToken) {
   4306                         loge("Failed to get tether update, force stop access point");
   4307                         transitionTo(mSoftApStartedState);
   4308                     }
   4309                     break;
   4310                 case CMD_START_SUPPLICANT:
   4311                 case CMD_STOP_SUPPLICANT:
   4312                 case CMD_START_AP:
   4313                 case CMD_STOP_AP:
   4314                 case CMD_START_DRIVER:
   4315                 case CMD_STOP_DRIVER:
   4316                 case CMD_SET_OPERATIONAL_MODE:
   4317                 case CMD_SET_COUNTRY_CODE:
   4318                 case CMD_SET_FREQUENCY_BAND:
   4319                 case CMD_START_PACKET_FILTERING:
   4320                 case CMD_STOP_PACKET_FILTERING:
   4321                     deferMessage(message);
   4322                     break;
   4323                 default:
   4324                     return NOT_HANDLED;
   4325             }
   4326             return HANDLED;
   4327         }
   4328     }
   4329 
   4330     //State machine initiated requests can have replyTo set to null indicating
   4331     //there are no recepients, we ignore those reply actions
   4332     private void replyToMessage(Message msg, int what) {
   4333         if (msg.replyTo == null) return;
   4334         Message dstMsg = obtainMessageWithArg2(msg);
   4335         dstMsg.what = what;
   4336         mReplyChannel.replyToMessage(msg, dstMsg);
   4337     }
   4338 
   4339     private void replyToMessage(Message msg, int what, int arg1) {
   4340         if (msg.replyTo == null) return;
   4341         Message dstMsg = obtainMessageWithArg2(msg);
   4342         dstMsg.what = what;
   4343         dstMsg.arg1 = arg1;
   4344         mReplyChannel.replyToMessage(msg, dstMsg);
   4345     }
   4346 
   4347     private void replyToMessage(Message msg, int what, Object obj) {
   4348         if (msg.replyTo == null) return;
   4349         Message dstMsg = obtainMessageWithArg2(msg);
   4350         dstMsg.what = what;
   4351         dstMsg.obj = obj;
   4352         mReplyChannel.replyToMessage(msg, dstMsg);
   4353     }
   4354 
   4355     /**
   4356      * arg2 on the source message has a unique id that needs to be retained in replies
   4357      * to match the request
   4358 
   4359      * see WifiManager for details
   4360      */
   4361     private Message obtainMessageWithArg2(Message srcMsg) {
   4362         Message msg = Message.obtain();
   4363         msg.arg2 = srcMsg.arg2;
   4364         return msg;
   4365     }
   4366 }
   4367