WifiStateMachine.java revision 3e351b0c2da2d53bf5b21ffb57df83813d6cc219
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
20import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
21import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
22import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
23import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
24import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
25import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
26import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
27import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
28/**
29 * TODO:
30 * Deprecate WIFI_STATE_UNKNOWN
31 */
32import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
33
34import android.app.ActivityManager;
35import android.app.AlarmManager;
36import android.app.AlertDialog;
37import android.app.PendingIntent;
38import android.app.backup.IBackupManager;
39import android.bluetooth.BluetoothAdapter;
40import android.content.BroadcastReceiver;
41import android.content.Context;
42import android.content.DialogInterface;
43import android.content.Intent;
44import android.content.IntentFilter;
45import android.content.pm.ApplicationInfo;
46import android.content.pm.PackageManager;
47import android.content.pm.PackageManager.NameNotFoundException;
48import android.database.ContentObserver;
49import android.net.ConnectivityManager;
50import android.net.DhcpResults;
51import android.net.BaseDhcpStateMachine;
52import android.net.DhcpStateMachine;
53import android.net.dhcp.DhcpClient;
54import android.net.InterfaceConfiguration;
55import android.net.LinkAddress;
56import android.net.LinkProperties;
57import android.net.NetworkAgent;
58import android.net.NetworkCapabilities;
59import android.net.NetworkFactory;
60import android.net.NetworkInfo;
61import android.net.NetworkInfo.DetailedState;
62import android.net.NetworkRequest;
63import android.net.NetworkUtils;
64import android.net.RouteInfo;
65import android.net.StaticIpConfiguration;
66import android.net.TrafficStats;
67import android.net.wifi.BatchedScanResult;
68import android.net.wifi.BatchedScanSettings;
69import android.net.wifi.RssiPacketCountInfo;
70import android.net.wifi.ScanResult;
71import android.net.wifi.ScanSettings;
72import android.net.wifi.SupplicantState;
73import android.net.wifi.WifiChannel;
74import android.net.wifi.WifiConfiguration;
75import android.net.wifi.WifiConnectionStatistics;
76import android.net.wifi.WifiEnterpriseConfig;
77import android.net.wifi.WifiInfo;
78import android.net.wifi.WifiLinkLayerStats;
79import android.net.wifi.WifiManager;
80import android.net.wifi.WifiSsid;
81import android.net.wifi.WpsInfo;
82import android.net.wifi.WpsResult;
83import android.net.wifi.WpsResult.Status;
84import android.net.wifi.p2p.IWifiP2pManager;
85import android.net.wifi.passpoint.IWifiPasspointManager;
86import android.os.BatteryStats;
87import android.os.Bundle;
88import android.os.IBinder;
89import android.os.INetworkManagementService;
90import android.os.Looper;
91import android.os.Message;
92import android.os.Messenger;
93import android.os.PowerManager;
94import android.os.Process;
95import android.os.RemoteException;
96import android.os.ServiceManager;
97import android.os.SystemClock;
98import android.os.SystemProperties;
99import android.os.UserHandle;
100import android.os.WorkSource;
101import android.provider.Settings;
102import android.telephony.TelephonyManager;
103import android.text.TextUtils;
104import android.util.Log;
105import android.util.LruCache;
106import android.view.WindowManager;
107
108import com.android.internal.R;
109import com.android.internal.app.IBatteryStats;
110import com.android.internal.util.AsyncChannel;
111import com.android.internal.util.Protocol;
112import com.android.internal.util.State;
113import com.android.internal.util.StateMachine;
114import com.android.server.net.NetlinkTracker;
115import com.android.server.wifi.hotspot2.NetworkDetail;
116import com.android.server.wifi.hotspot2.SupplicantBridge;
117import com.android.server.wifi.p2p.WifiP2pServiceImpl;
118
119import java.io.BufferedReader;
120import java.io.FileDescriptor;
121import java.io.FileNotFoundException;
122import java.io.FileReader;
123import java.io.IOException;
124import java.io.PrintWriter;
125import java.net.Inet4Address;
126import java.net.InetAddress;
127import java.net.SocketException;
128import java.nio.BufferUnderflowException;
129import java.util.ArrayList;
130import java.util.Calendar;
131import java.util.HashSet;
132import java.util.LinkedList;
133import java.util.List;
134import java.util.Locale;
135import java.util.Queue;
136import java.util.Random;
137import java.util.concurrent.atomic.AtomicBoolean;
138import java.util.concurrent.atomic.AtomicInteger;
139import java.util.regex.Matcher;
140import java.util.regex.Pattern;
141
142/**
143 * Track the state of Wifi connectivity. All event handling is done here,
144 * and all changes in connectivity state are initiated here.
145 *
146 * Wi-Fi now supports three modes of operation: Client, SoftAp and p2p
147 * In the current implementation, we support concurrent wifi p2p and wifi operation.
148 * The WifiStateMachine handles SoftAp and Client operations while WifiP2pService
149 * handles p2p operation.
150 *
151 * @hide
152 */
153public class WifiStateMachine extends StateMachine {
154
155    private static final String NETWORKTYPE = "WIFI";
156    private static final String NETWORKTYPE_UNTRUSTED = "WIFI_UT";
157    private static boolean DBG = false;
158    private static boolean VDBG = false;
159    private static boolean VVDBG = false;
160    private static boolean mLogMessages = false;
161    private static final String TAG = "WifiStateMachine";
162
163    private static final int ONE_HOUR_MILLI = 1000 * 60 * 60;
164
165    private static final String GOOGLE_OUI = "DA-A1-19";
166
167    /* temporary debug flag - best network selection development */
168    private static boolean PDBG = false;
169
170    /* debug flag, indicating if handling of ASSOCIATION_REJECT ended up blacklisting
171     * the corresponding BSSID.
172     */
173    private boolean didBlackListBSSID = false;
174
175    /**
176     * Log with error attribute
177     *
178     * @param s is string log
179     */
180    protected void loge(String s) {
181        Log.e(getName(), s);
182    }
183    protected void logd(String s) {
184        Log.d(getName(), s);
185    }
186    protected void log(String s) {;
187        Log.d(getName(), s);
188    }
189
190    private WifiMonitor mWifiMonitor;
191    private WifiNative mWifiNative;
192    private WifiConfigStore mWifiConfigStore;
193    private WifiAutoJoinController mWifiAutoJoinController;
194    private INetworkManagementService mNwService;
195    private ConnectivityManager mCm;
196    private WifiLogger mWifiLogger;
197
198    private final boolean mP2pSupported;
199    private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
200    private boolean mTemporarilyDisconnectWifi = false;
201    private final String mPrimaryDeviceType;
202
203    /* Scan results handling */
204    private List<ScanDetail> mScanResults = new ArrayList<>();
205    private static final Pattern scanResultPattern = Pattern.compile("\t+");
206    private static final int SCAN_RESULT_CACHE_SIZE = 160;
207    private final LruCache<NetworkDetail, ScanDetail> mScanResultCache;
208    // For debug, number of known scan results that were found as part of last scan result event,
209    // as well the number of scans results returned by the supplicant with that message
210    private int mNumScanResultsKnown;
211    private int mNumScanResultsReturned;
212
213    /* Batch scan results */
214    private final List<BatchedScanResult> mBatchedScanResults =
215            new ArrayList<BatchedScanResult>();
216    private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE;
217    private int mExpectedBatchedScans = 0;
218    private long mBatchedScanMinPollTime = 0;
219
220    private boolean mScreenOn = false;
221
222    /* Chipset supports background scan */
223    private final boolean mBackgroundScanSupported;
224
225    private String mInterfaceName;
226    /* Tethering interface could be separate from wlan interface */
227    private String mTetherInterfaceName;
228
229    private int mLastSignalLevel = -1;
230    private String mLastBssid;
231    private int mLastNetworkId; // The network Id we successfully joined
232    private boolean linkDebouncing = false;
233
234    // Testing various network disconnect cases by sending lots of spurious
235    // disconnect to supplicant
236    private boolean testNetworkDisconnect = false;
237
238    private boolean mEnableRssiPolling = false;
239    private boolean mEnableBackgroundScan = false;
240    private int mRssiPollToken = 0;
241    /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
242    * In CONNECT_MODE, the STA can scan and connect to an access point
243    * In SCAN_ONLY_MODE, the STA can only scan for access points
244    * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off
245    */
246    private int mOperationalMode = CONNECT_MODE;
247    private boolean mIsScanOngoing = false;
248    private boolean mIsFullScanOngoing = false;
249    private boolean mSendScanResultsBroadcast = false;
250
251    private final Queue<Message> mBufferedScanMsg = new LinkedList<Message>();
252    private WorkSource mScanWorkSource = null;
253    private static final int UNKNOWN_SCAN_SOURCE = -1;
254    private static final int SCAN_ALARM_SOURCE = -2;
255    private static final int ADD_OR_UPDATE_SOURCE = -3;
256    private static final int SET_ALLOW_UNTRUSTED_SOURCE = -4;
257    private static final int ENABLE_WIFI = -5;
258    public static final int DFS_RESTRICTED_SCAN_REQUEST = -6;
259
260    private static final int SCAN_REQUEST_BUFFER_MAX_SIZE = 10;
261    private static final String CUSTOMIZED_SCAN_SETTING = "customized_scan_settings";
262    private static final String CUSTOMIZED_SCAN_WORKSOURCE = "customized_scan_worksource";
263    private static final String SCAN_REQUEST_TIME = "scan_request_time";
264
265    private static final String BATCHED_SETTING = "batched_settings";
266    private static final String BATCHED_WORKSOURCE = "batched_worksource";
267
268    /* Tracks if state machine has received any screen state change broadcast yet.
269     * We can miss one of these at boot.
270     */
271    private AtomicBoolean mScreenBroadcastReceived = new AtomicBoolean(false);
272
273    private boolean mBluetoothConnectionActive = false;
274
275    private PowerManager.WakeLock mSuspendWakeLock;
276
277    /**
278     * Interval in milliseconds between polling for RSSI
279     * and linkspeed information
280     */
281    private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
282
283    /**
284     * Interval in milliseconds between receiving a disconnect event
285     * while connected to a good AP, and handling the disconnect proper
286     */
287    private static final int LINK_FLAPPING_DEBOUNCE_MSEC = 7000;
288
289    /**
290     * Delay between supplicant restarts upon failure to establish connection
291     */
292    private static final int SUPPLICANT_RESTART_INTERVAL_MSECS = 5000;
293
294    /**
295     * Number of times we attempt to restart supplicant
296     */
297    private static final int SUPPLICANT_RESTART_TRIES = 5;
298
299    private int mSupplicantRestartCount = 0;
300    /* Tracks sequence number on stop failure message */
301    private int mSupplicantStopFailureToken = 0;
302
303    /**
304     * Tether state change notification time out
305     */
306    private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
307
308    /* Tracks sequence number on a tether notification time out */
309    private int mTetherToken = 0;
310
311    /**
312     * Driver start time out.
313     */
314    private static final int DRIVER_START_TIME_OUT_MSECS = 10000;
315
316    /* Tracks sequence number on a driver time out */
317    private int mDriverStartToken = 0;
318
319    /**
320     * The link properties of the wifi interface.
321     * Do not modify this directly; use updateLinkProperties instead.
322     */
323    private LinkProperties mLinkProperties;
324
325    /* Tracks sequence number on a periodic scan message */
326    private int mPeriodicScanToken = 0;
327
328    // Wakelock held during wifi start/stop and driver load/unload
329    private PowerManager.WakeLock mWakeLock;
330
331    private Context mContext;
332
333    private final Object mDhcpResultsLock = new Object();
334    private DhcpResults mDhcpResults;
335    private WifiInfo mWifiInfo;
336    private NetworkInfo mNetworkInfo;
337    private NetworkCapabilities mNetworkCapabilities;
338    private SupplicantStateTracker mSupplicantStateTracker;
339    private BaseDhcpStateMachine mDhcpStateMachine;
340    private boolean mDhcpActive = false;
341
342    private int mWifiLinkLayerStatsSupported = 4; // Temporary disable
343
344    private final AtomicInteger mCountryCodeSequence = new AtomicInteger();
345
346    // Whether the state machine goes thru the Disconnecting->Disconnected->ObtainingIpAddress
347    private int mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
348
349    // Roaming failure count
350    private int mRoamFailCount = 0;
351
352    // This is the BSSID we are trying to associate to, it can be set to "any"
353    // if we havent selected a BSSID for joining.
354    // if we havent selected a BSSID for joining.
355    // The BSSID we are associated to is found in mWifiInfo
356    private String mTargetRoamBSSID = "any";
357
358    private long mLastDriverRoamAttempt = 0;
359
360    private WifiConfiguration targetWificonfiguration = null;
361
362    // Used as debug to indicate which configuration last was saved
363    private WifiConfiguration lastSavedConfigurationAttempt = null;
364
365    // Used as debug to indicate which configuration last was removed
366    private WifiConfiguration lastForgetConfigurationAttempt = null;
367
368    //Random used by softAP channel Selection
369    private static Random mRandom = new Random(Calendar.getInstance().getTimeInMillis());
370    boolean isRoaming() {
371        return mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_ROAMING
372                || mAutoRoaming == WifiAutoJoinController.AUTO_JOIN_EXTENDED_ROAMING;
373    }
374
375    public void autoRoamSetBSSID(int netId, String bssid) {
376        autoRoamSetBSSID(mWifiConfigStore.getWifiConfiguration(netId), bssid);
377    }
378
379    public boolean autoRoamSetBSSID(WifiConfiguration config, String bssid) {
380        boolean ret = true;
381        if (mTargetRoamBSSID == null) mTargetRoamBSSID = "any";
382        if (bssid == null) bssid = "any";
383        if (config == null) return false; // Nothing to do
384
385        if (mTargetRoamBSSID != null && bssid == mTargetRoamBSSID && bssid == config.BSSID) {
386            return false; // We didnt change anything
387        }
388        if (!mTargetRoamBSSID.equals("any") && bssid.equals("any")) {
389            // Changing to ANY
390            if (!mWifiConfigStore.roamOnAny) {
391                ret =  false; // Nothing to do
392            }
393        }
394        if (VDBG) {
395           loge("autoRoamSetBSSID " + bssid
396                   + " key=" + config.configKey());
397        }
398        config.autoJoinBSSID = bssid;
399        mTargetRoamBSSID = bssid;
400        mWifiConfigStore.saveWifiConfigBSSID(config);
401        return ret;
402    }
403
404    /**
405     * Save the UID correctly depending on if this is a new or existing network.
406     * @return true if operation is authorized, false otherwise
407     */
408    boolean recordUidIfAuthorized(WifiConfiguration config, int uid) {
409        if (!mWifiConfigStore.isNetworkConfigured(config)) {
410            config.creatorUid = uid;
411            config.creatorName = mContext.getPackageManager().getNameForUid(uid);
412        } else if (!mWifiConfigStore.canModifyNetwork(uid, config)) {
413            return false;
414        }
415
416        config.lastUpdateUid = uid;
417        config.lastUpdateName = mContext.getPackageManager().getNameForUid(uid);
418
419        return true;
420
421    }
422
423    /**
424     * Checks to see if user has specified if the apps configuration is connectable.
425     * If the user hasn't specified we query the user and return true.
426     *
427     * @param message The message to be deffered
428     * @param netId Network id of the configuration to check against
429     * @param allowOverride If true we won't defer to the user if the uid of the message holds the
430     *                      CONFIG_OVERRIDE_PERMISSION
431     * @return True if we are waiting for user feedback or netId is invalid. False otherwise.
432     */
433    boolean deferForUserInput(Message message, int netId, boolean allowOverride){
434        final WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(netId);
435
436        // We can only evaluate saved configurations.
437        if (config == null) {
438            loge("deferForUserInput: configuration for netId="+netId+" not stored");
439            return true;
440        }
441
442        switch (config.userApproved) {
443            case WifiConfiguration.USER_APPROVED:
444            case WifiConfiguration.USER_BANNED:
445                return false;
446            case WifiConfiguration.USER_PENDING:
447            default: // USER_UNSPECIFIED
448               /* the intention was to ask user here; but a dialog box is   *
449                * too invasive; so we are going to allow connection for now */
450                config.userApproved = WifiConfiguration.USER_APPROVED;
451                return false;
452        }
453    }
454
455    /**
456     * Subset of link properties coming from netlink.
457     * Currently includes IPv4 and IPv6 addresses. In the future will also include IPv6 DNS servers
458     * and domains obtained from router advertisements (RFC 6106).
459     */
460    private NetlinkTracker mNetlinkTracker;
461
462    private AlarmManager mAlarmManager;
463    private PendingIntent mScanIntent;
464    private PendingIntent mDriverStopIntent;
465    private PendingIntent mBatchedScanIntervalIntent;
466
467    /* Tracks current frequency mode */
468    private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
469
470    /* Tracks if we are filtering Multicast v4 packets. Default is to filter. */
471    private AtomicBoolean mFilteringMulticastV4Packets = new AtomicBoolean(true);
472
473    // Channel for sending replies.
474    private AsyncChannel mReplyChannel = new AsyncChannel();
475
476    private WifiP2pServiceImpl mWifiP2pServiceImpl;
477
478    // Used to initiate a connection with WifiP2pService
479    private AsyncChannel mWifiP2pChannel;
480    private AsyncChannel mWifiApConfigChannel;
481
482    private int mConnectionRequests = 0;
483    private WifiNetworkFactory mNetworkFactory;
484    private UntrustedWifiNetworkFactory mUntrustedNetworkFactory;
485    private WifiNetworkAgent mNetworkAgent;
486
487    // Keep track of various statistics, for retrieval by System Apps, i.e. under @SystemApi
488    // We should really persist that into the networkHistory.txt file, and read it back when
489    // WifiStateMachine starts up
490    private WifiConnectionStatistics mWifiConnectionStatistics = new WifiConnectionStatistics();
491
492    // Used to filter out requests we couldn't possibly satisfy.
493    private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities();
494
495    /* The base for wifi message types */
496    static final int BASE = Protocol.BASE_WIFI;
497    /* Start the supplicant */
498    static final int CMD_START_SUPPLICANT                 = BASE + 11;
499    /* Stop the supplicant */
500    static final int CMD_STOP_SUPPLICANT                  = BASE + 12;
501    /* Start the driver */
502    static final int CMD_START_DRIVER                     = BASE + 13;
503    /* Stop the driver */
504    static final int CMD_STOP_DRIVER                      = BASE + 14;
505    /* Indicates Static IP succeeded */
506    static final int CMD_STATIC_IP_SUCCESS                = BASE + 15;
507    /* Indicates Static IP failed */
508    static final int CMD_STATIC_IP_FAILURE                = BASE + 16;
509    /* Indicates supplicant stop failed */
510    static final int CMD_STOP_SUPPLICANT_FAILED           = BASE + 17;
511    /* Delayed stop to avoid shutting down driver too quick*/
512    static final int CMD_DELAYED_STOP_DRIVER              = BASE + 18;
513    /* A delayed message sent to start driver when it fail to come up */
514    static final int CMD_DRIVER_START_TIMED_OUT           = BASE + 19;
515
516    /* Start the soft access point */
517    static final int CMD_START_AP                         = BASE + 21;
518    /* Indicates soft ap start succeeded */
519    static final int CMD_START_AP_SUCCESS                 = BASE + 22;
520    /* Indicates soft ap start failed */
521    static final int CMD_START_AP_FAILURE                 = BASE + 23;
522    /* Stop the soft access point */
523    static final int CMD_STOP_AP                          = BASE + 24;
524    /* Set the soft access point configuration */
525    static final int CMD_SET_AP_CONFIG                    = BASE + 25;
526    /* Soft access point configuration set completed */
527    static final int CMD_SET_AP_CONFIG_COMPLETED          = BASE + 26;
528    /* Request the soft access point configuration */
529    static final int CMD_REQUEST_AP_CONFIG                = BASE + 27;
530    /* Response to access point configuration request */
531    static final int CMD_RESPONSE_AP_CONFIG               = BASE + 28;
532    /* Invoked when getting a tether state change notification */
533    static final int CMD_TETHER_STATE_CHANGE              = BASE + 29;
534    /* A delayed message sent to indicate tether state change failed to arrive */
535    static final int CMD_TETHER_NOTIFICATION_TIMED_OUT    = BASE + 30;
536
537    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 31;
538
539    /* Supplicant commands */
540    /* Is supplicant alive ? */
541    static final int CMD_PING_SUPPLICANT                  = BASE + 51;
542    /* Add/update a network configuration */
543    static final int CMD_ADD_OR_UPDATE_NETWORK            = BASE + 52;
544    /* Delete a network */
545    static final int CMD_REMOVE_NETWORK                   = BASE + 53;
546    /* Enable a network. The device will attempt a connection to the given network. */
547    static final int CMD_ENABLE_NETWORK                   = BASE + 54;
548    /* Enable all networks */
549    static final int CMD_ENABLE_ALL_NETWORKS              = BASE + 55;
550    /* Blacklist network. De-prioritizes the given BSSID for connection. */
551    static final int CMD_BLACKLIST_NETWORK                = BASE + 56;
552    /* Clear the blacklist network list */
553    static final int CMD_CLEAR_BLACKLIST                  = BASE + 57;
554    /* Save configuration */
555    static final int CMD_SAVE_CONFIG                      = BASE + 58;
556    /* Get configured networks */
557    static final int CMD_GET_CONFIGURED_NETWORKS          = BASE + 59;
558    /* Get available frequencies */
559    static final int CMD_GET_CAPABILITY_FREQ              = BASE + 60;
560    /* Get adaptors */
561    static final int CMD_GET_SUPPORTED_FEATURES           = BASE + 61;
562    /* Get configured networks with real preSharedKey */
563    static final int CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS = BASE + 62;
564    /* Get Link Layer Stats thru HAL */
565    static final int CMD_GET_LINK_LAYER_STATS             = BASE + 63;
566    /* Supplicant commands after driver start*/
567    /* Initiate a scan */
568    static final int CMD_START_SCAN                       = BASE + 71;
569    /* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
570    static final int CMD_SET_OPERATIONAL_MODE             = BASE + 72;
571    /* Disconnect from a network */
572    static final int CMD_DISCONNECT                       = BASE + 73;
573    /* Reconnect to a network */
574    static final int CMD_RECONNECT                        = BASE + 74;
575    /* Reassociate to a network */
576    static final int CMD_REASSOCIATE                      = BASE + 75;
577    /* Get Connection Statistis */
578    static final int CMD_GET_CONNECTION_STATISTICS        = BASE + 76;
579
580    /* Controls suspend mode optimizations
581     *
582     * When high perf mode is enabled, suspend mode optimizations are disabled
583     *
584     * When high perf mode is disabled, suspend mode optimizations are enabled
585     *
586     * Suspend mode optimizations include:
587     * - packet filtering
588     * - turn off roaming
589     * - DTIM wake up settings
590     */
591    static final int CMD_SET_HIGH_PERF_MODE               = BASE + 77;
592    /* Set the country code */
593    static final int CMD_SET_COUNTRY_CODE                 = BASE + 80;
594    /* Enables RSSI poll */
595    static final int CMD_ENABLE_RSSI_POLL                 = BASE + 82;
596    /* RSSI poll */
597    static final int CMD_RSSI_POLL                        = BASE + 83;
598    /* Set up packet filtering */
599    static final int CMD_START_PACKET_FILTERING           = BASE + 84;
600    /* Clear packet filter */
601    static final int CMD_STOP_PACKET_FILTERING            = BASE + 85;
602    /* Enable suspend mode optimizations in the driver */
603    static final int CMD_SET_SUSPEND_OPT_ENABLED          = BASE + 86;
604    /* Delayed NETWORK_DISCONNECT */
605    static final int CMD_DELAYED_NETWORK_DISCONNECT       = BASE + 87;
606    /* When there are no saved networks, we do a periodic scan to notify user of
607     * an open network */
608    static final int CMD_NO_NETWORKS_PERIODIC_SCAN        = BASE + 88;
609    /* Test network Disconnection NETWORK_DISCONNECT */
610    static final int CMD_TEST_NETWORK_DISCONNECT          = BASE + 89;
611    private int testNetworkDisconnectCounter = 0;
612
613    /* arg1 values to CMD_STOP_PACKET_FILTERING and CMD_START_PACKET_FILTERING */
614    static final int MULTICAST_V6  = 1;
615    static final int MULTICAST_V4  = 0;
616
617   /* Set the frequency band */
618    static final int CMD_SET_FREQUENCY_BAND               = BASE + 90;
619    /* Enable TDLS on a specific MAC address */
620    static final int CMD_ENABLE_TDLS                      = BASE + 92;
621    /* DHCP/IP configuration watchdog */
622    static final int CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER    = BASE + 93;
623    /* Remove a packages associated configrations */
624    static final int CMD_REMOVE_APP_CONFIGURATIONS        = BASE + 97;
625    /* Remove a packages associated configrations */
626    static final int CMD_REMOVE_USER_CONFIGURATIONS       = BASE + 149;
627
628    /**
629     * Make this timer 40 seconds, which is about the normal DHCP timeout.
630     * In no valid case, the WiFiStateMachine should remain stuck in ObtainingIpAddress
631     * for more than 30 seconds.
632     */
633    static final int OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC = 40000;
634
635    int obtainingIpWatchdogCount = 0;
636
637    /* Commands from/to the SupplicantStateTracker */
638    /* Reset the supplicant state tracker */
639    static final int CMD_RESET_SUPPLICANT_STATE           = BASE + 111;
640
641
642    /**
643     * Watchdog for protecting against b/16823537
644     * Leave time for 4-ways handshake to succeed
645     */
646    static final int ROAM_GUARD_TIMER_MSEC = 15000;
647
648    int roamWatchdogCount = 0;
649    /* Roam state watchdog */
650    static final int CMD_ROAM_WATCHDOG_TIMER    = BASE + 94;
651    /* Screen change intent handling */
652    static final int CMD_SCREEN_STATE_CHANGED              = BASE + 95;
653
654    int disconnectingWatchdogCount = 0;
655    static final int DISCONNECTING_GUARD_TIMER_MSEC = 5000;
656
657    /* Disconnecting state watchdog */
658    static final int CMD_DISCONNECTING_WATCHDOG_TIMER     = BASE + 96;
659
660    /* Disable an ephemeral network */
661    static final int CMD_DISABLE_EPHEMERAL_NETWORK = BASE + 98;
662
663    /* Get matching network */
664    static final int CMD_GET_MATCHING_CONFIG              = BASE + 99;
665
666    /* P2p commands */
667    /* We are ok with no response here since we wont do much with it anyway */
668    public static final int CMD_ENABLE_P2P                = BASE + 131;
669    /* In order to shut down supplicant cleanly, we wait till p2p has
670     * been disabled */
671    public static final int CMD_DISABLE_P2P_REQ           = BASE + 132;
672    public static final int CMD_DISABLE_P2P_RSP           = BASE + 133;
673
674    public static final int CMD_BOOT_COMPLETED            = BASE + 134;
675
676    /* change the batch scan settings.
677     * arg1 = responsible UID
678     * arg2 = csph (channel scans per hour)
679     * obj = bundle with the new settings and the optional worksource
680     */
681    public static final int CMD_SET_BATCHED_SCAN          = BASE + 135;
682    public static final int CMD_START_NEXT_BATCHED_SCAN   = BASE + 136;
683    public static final int CMD_POLL_BATCHED_SCAN         = BASE + 137;
684
685    /* We now have a valid IP configuration. */
686    static final int CMD_IP_CONFIGURATION_SUCCESSFUL      = BASE + 138;
687    /* We no longer have a valid IP configuration. */
688    static final int CMD_IP_CONFIGURATION_LOST            = BASE + 139;
689    /* Link configuration (IP address, DNS, ...) changes notified via netlink */
690    static final int CMD_UPDATE_LINKPROPERTIES            = BASE + 140;
691
692    /* Supplicant is trying to associate to a given BSSID */
693    static final int CMD_TARGET_BSSID                     = BASE + 141;
694
695    /* Reload all networks and reconnect */
696    static final int CMD_RELOAD_TLS_AND_RECONNECT         = BASE + 142;
697
698    static final int CMD_AUTO_CONNECT                     = BASE + 143;
699
700    static final int network_status_unwanted_disconnect = 0;
701    static final int network_status_unwanted_disable_autojoin = 1;
702
703    static final int CMD_UNWANTED_NETWORK                 = BASE + 144;
704
705    static final int CMD_AUTO_ROAM                        = BASE + 145;
706
707    static final int CMD_AUTO_SAVE_NETWORK                = BASE + 146;
708
709    static final int CMD_ASSOCIATED_BSSID                 = BASE + 147;
710
711    static final int CMD_NETWORK_STATUS                   = BASE + 148;
712
713    static final int CMD_ACCEPT_UNVALIDATED               = BASE + 149;
714
715    /* Wifi state machine modes of operation */
716    /* CONNECT_MODE - connect to any 'known' AP when it becomes available */
717    public static final int CONNECT_MODE                   = 1;
718    /* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */
719    public static final int SCAN_ONLY_MODE                 = 2;
720    /* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */
721    public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE   = 3;
722
723    private static final int SUCCESS = 1;
724    private static final int FAILURE = -1;
725
726    /* Tracks if suspend optimizations need to be disabled by DHCP,
727     * screen or due to high perf mode.
728     * When any of them needs to disable it, we keep the suspend optimizations
729     * disabled
730     */
731    private int mSuspendOptNeedsDisabled = 0;
732
733    private static final int SUSPEND_DUE_TO_DHCP       = 1;
734    private static final int SUSPEND_DUE_TO_HIGH_PERF  = 1<<1;
735    private static final int SUSPEND_DUE_TO_SCREEN     = 1<<2;
736
737    /* Tracks if user has enabled suspend optimizations through settings */
738    private AtomicBoolean mUserWantsSuspendOpt = new AtomicBoolean(true);
739
740    /**
741     * Default framework scan interval in milliseconds. This is used in the scenario in which
742     * wifi chipset does not support background scanning to set up a
743     * periodic wake up scan so that the device can connect to a new access
744     * point on the move. {@link Settings.Global#WIFI_FRAMEWORK_SCAN_INTERVAL_MS} can
745     * override this.
746     */
747    private final int mDefaultFrameworkScanIntervalMs;
748
749    private int mDisconnectedScanPeriodMs = 10000;
750
751    /**
752     * Supplicant scan interval in milliseconds.
753     * Comes from {@link Settings.Global#WIFI_SUPPLICANT_SCAN_INTERVAL_MS} or
754     * from the default config if the setting is not set
755     */
756    private long mSupplicantScanIntervalMs;
757
758    /**
759     * timeStamp of last full band scan we perfoemed for autojoin while connected with screen lit
760     */
761    private long lastFullBandConnectedTimeMilli;
762
763    /**
764     * time interval to the next full band scan we will perform for
765     * autojoin while connected with screen lit
766     */
767    private long fullBandConnectedTimeIntervalMilli;
768
769    /**
770     * max time interval to the next full band scan we will perform for
771     * autojoin while connected with screen lit
772     * Max time is 5 minutes
773     */
774    private static final long  maxFullBandConnectedTimeIntervalMilli = 1000 * 60 * 5;
775
776    /**
777     * Minimum time interval between enabling all networks.
778     * A device can end up repeatedly connecting to a bad network on screen on/off toggle
779     * due to enabling every time. We add a threshold to avoid this.
780     */
781    private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
782    private long mLastEnableAllNetworksTime;
783
784    int mRunningBeaconCount = 0;
785
786    /**
787     * Starting and shutting down driver too quick causes problems leading to driver
788     * being in a bad state. Delay driver stop.
789     */
790    private final int mDriverStopDelayMs;
791    private int mDelayedStopCounter;
792    private boolean mInDelayedStop = false;
793
794    // there is a delay between StateMachine change country code and Supplicant change country code
795    // here save the current WifiStateMachine set country code
796    private volatile String mSetCountryCode = null;
797
798    // Supplicant doesn't like setting the same country code multiple times (it may drop
799    // currently connected network), so we save the current device set country code here to avoid
800    // redundency
801    private String mDriverSetCountryCode = null;
802
803    /* Default parent state */
804    private State mDefaultState = new DefaultState();
805    /* Temporary initial state */
806    private State mInitialState = new InitialState();
807    /* Driver loaded, waiting for supplicant to start */
808    private State mSupplicantStartingState = new SupplicantStartingState();
809    /* Driver loaded and supplicant ready */
810    private State mSupplicantStartedState = new SupplicantStartedState();
811    /* Waiting for supplicant to stop and monitor to exit */
812    private State mSupplicantStoppingState = new SupplicantStoppingState();
813    /* Driver start issued, waiting for completed event */
814    private State mDriverStartingState = new DriverStartingState();
815    /* Driver started */
816    private State mDriverStartedState = new DriverStartedState();
817    /* Wait until p2p is disabled
818     * This is a special state which is entered right after we exit out of DriverStartedState
819     * before transitioning to another state.
820     */
821    private State mWaitForP2pDisableState = new WaitForP2pDisableState();
822    /* Driver stopping */
823    private State mDriverStoppingState = new DriverStoppingState();
824    /* Driver stopped */
825    private State mDriverStoppedState = new DriverStoppedState();
826    /* Scan for networks, no connection will be established */
827    private State mScanModeState = new ScanModeState();
828    /* Connecting to an access point */
829    private State mConnectModeState = new ConnectModeState();
830    /* Connected at 802.11 (L2) level */
831    private State mL2ConnectedState = new L2ConnectedState();
832    /* fetching IP after connection to access point (assoc+auth complete) */
833    private State mObtainingIpState = new ObtainingIpState();
834    /* Waiting for link quality verification to be complete */
835    private State mVerifyingLinkState = new VerifyingLinkState();
836    /* Connected with IP addr */
837    private State mConnectedState = new ConnectedState();
838    /* Roaming */
839    private State mRoamingState = new RoamingState();
840    /* disconnect issued, waiting for network disconnect confirmation */
841    private State mDisconnectingState = new DisconnectingState();
842    /* Network is not connected, supplicant assoc+auth is not complete */
843    private State mDisconnectedState = new DisconnectedState();
844    /* Waiting for WPS to be completed*/
845    private State mWpsRunningState = new WpsRunningState();
846
847    /* Soft ap is starting up */
848    private State mSoftApStartingState = new SoftApStartingState();
849    /* Soft ap is running */
850    private State mSoftApStartedState = new SoftApStartedState();
851    /* Soft ap is running and we are waiting for tether notification */
852    private State mTetheringState = new TetheringState();
853    /* Soft ap is running and we are tethered through connectivity service */
854    private State mTetheredState = new TetheredState();
855    /* Waiting for untether confirmation before stopping soft Ap */
856    private State mUntetheringState = new UntetheringState();
857
858    private class TetherStateChange {
859        ArrayList<String> available;
860        ArrayList<String> active;
861        TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
862            available = av;
863            active = ac;
864        }
865    }
866
867    public static class SimAuthRequestData {
868        int networkId;
869        int protocol;
870        String ssid;
871        // EAP-SIM: data[] contains the 3 rand, one for each of the 3 challenges
872        // EAP-AKA/AKA': data[] contains rand & authn couple for the single challenge
873        String[] data;
874    }
875
876    /**
877     * One of  {@link WifiManager#WIFI_STATE_DISABLED},
878     *         {@link WifiManager#WIFI_STATE_DISABLING},
879     *         {@link WifiManager#WIFI_STATE_ENABLED},
880     *         {@link WifiManager#WIFI_STATE_ENABLING},
881     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
882     *
883     */
884    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
885
886    /**
887     * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
888     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
889     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
890     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
891     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
892     *
893     */
894    private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
895
896    private static final int SCAN_REQUEST = 0;
897    private static final String ACTION_START_SCAN =
898        "com.android.server.WifiManager.action.START_SCAN";
899
900    private static final String DELAYED_STOP_COUNTER = "DelayedStopCounter";
901    private static final int DRIVER_STOP_REQUEST = 0;
902    private static final String ACTION_DELAYED_DRIVER_STOP =
903        "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
904
905    private static final String ACTION_REFRESH_BATCHED_SCAN =
906            "com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN";
907    /**
908     * Keep track of whether WIFI is running.
909     */
910    private boolean mIsRunning = false;
911
912    /**
913     * Keep track of whether we last told the battery stats we had started.
914     */
915    private boolean mReportedRunning = false;
916
917    /**
918     * Most recently set source of starting WIFI.
919     */
920    private final WorkSource mRunningWifiUids = new WorkSource();
921
922    /**
923     * The last reported UIDs that were responsible for starting WIFI.
924     */
925    private final WorkSource mLastRunningWifiUids = new WorkSource();
926
927    private final IBatteryStats mBatteryStats;
928
929    private BatchedScanSettings mBatchedScanSettings = null;
930
931    /**
932     * Track the worksource/cost of the current settings and track what's been noted
933     * to the battery stats, so we can mark the end of the previous when changing.
934     */
935    private WorkSource mBatchedScanWorkSource = null;
936    private int mBatchedScanCsph = 0;
937    private WorkSource mNotedBatchedScanWorkSource = null;
938    private int mNotedBatchedScanCsph = 0;
939
940    private String mTcpBufferSizes = null;
941
942    // Used for debug and stats gathering
943    private static int sScanAlarmIntentCount = 0;
944
945    final static int frameworkMinScanIntervalSaneValue = 10000;
946
947    public WifiStateMachine(Context context, String wlanInterface,
948            WifiTrafficPoller trafficPoller){
949        super("WifiStateMachine");
950        mContext = context;
951        mSetCountryCode = Settings.Global.getString(
952                mContext.getContentResolver(), Settings.Global.WIFI_COUNTRY_CODE);
953        mInterfaceName = wlanInterface;
954        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
955        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
956                BatteryStats.SERVICE_NAME));
957
958        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
959        mNwService = INetworkManagementService.Stub.asInterface(b);
960
961        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
962                PackageManager.FEATURE_WIFI_DIRECT);
963
964        mWifiNative = new WifiNative(mInterfaceName);
965        mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
966        mWifiAutoJoinController = new WifiAutoJoinController(context, this,
967                mWifiConfigStore, mWifiConnectionStatistics, mWifiNative);
968        mWifiLogger = new WifiLogger(mContext, this);
969        mWifiMonitor = new WifiMonitor(this, mWifiNative);
970        mWifiInfo = new WifiInfo();
971        mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
972                getHandler());
973        mLinkProperties = new LinkProperties();
974
975        IBinder s1 = ServiceManager.getService(Context.WIFI_P2P_SERVICE);
976        mWifiP2pServiceImpl = (WifiP2pServiceImpl)IWifiP2pManager.Stub.asInterface(s1);
977
978        IBinder s2 = ServiceManager.getService(Context.WIFI_PASSPOINT_SERVICE);
979
980        mNetworkInfo.setIsAvailable(false);
981        mLastBssid = null;
982        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
983        mLastSignalLevel = -1;
984
985        mNetlinkTracker = new NetlinkTracker(mInterfaceName, new NetlinkTracker.Callback() {
986            public void update() {
987                sendMessage(CMD_UPDATE_LINKPROPERTIES);
988            }
989        });
990        try {
991            mNwService.registerObserver(mNetlinkTracker);
992        } catch (RemoteException e) {
993            loge("Couldn't register netlink tracker: " + e.toString());
994        }
995
996        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
997        mScanIntent = getPrivateBroadcast(ACTION_START_SCAN, SCAN_REQUEST);
998        mBatchedScanIntervalIntent = getPrivateBroadcast(ACTION_REFRESH_BATCHED_SCAN, 0);
999
1000        // Make sure the interval is not configured less than 10 seconds
1001        int period = mContext.getResources().getInteger(
1002                R.integer.config_wifi_framework_scan_interval);
1003        if (period < frameworkMinScanIntervalSaneValue) {
1004            period = frameworkMinScanIntervalSaneValue;
1005        }
1006        mDefaultFrameworkScanIntervalMs = period;
1007        mDriverStopDelayMs = mContext.getResources().getInteger(
1008                R.integer.config_wifi_driver_stop_delay);
1009
1010        mBackgroundScanSupported = mContext.getResources().getBoolean(
1011                R.bool.config_wifi_background_scan_support);
1012
1013        mPrimaryDeviceType = mContext.getResources().getString(
1014                R.string.config_wifi_p2p_device_type);
1015
1016        mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
1017                    Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
1018
1019        mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
1020        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
1021        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
1022        mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(1024 * 1024);
1023        mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(1024 * 1024);
1024        // TODO - needs to be a bit more dynamic
1025        mNetworkCapabilities = new NetworkCapabilities(mNetworkCapabilitiesFilter);
1026
1027        mContext.registerReceiver(
1028            new BroadcastReceiver() {
1029                @Override
1030                public void onReceive(Context context, Intent intent) {
1031                    ArrayList<String> available = intent.getStringArrayListExtra(
1032                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);
1033                    ArrayList<String> active = intent.getStringArrayListExtra(
1034                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
1035                    sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active));
1036                }
1037            },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
1038
1039        mContext.registerReceiver(
1040                new BroadcastReceiver() {
1041                    @Override
1042                    public void onReceive(Context context, Intent intent) {
1043                        sScanAlarmIntentCount++; // Used for debug only
1044                        startScan(SCAN_ALARM_SOURCE, mDelayedScanCounter.incrementAndGet(), null, null);
1045                        if (VDBG)
1046                            loge("WiFiStateMachine SCAN ALARM -> " + mDelayedScanCounter.get());
1047                    }
1048                },
1049                new IntentFilter(ACTION_START_SCAN));
1050
1051        IntentFilter filter = new IntentFilter();
1052        filter.addAction(Intent.ACTION_SCREEN_ON);
1053        filter.addAction(Intent.ACTION_SCREEN_OFF);
1054        filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
1055        mContext.registerReceiver(
1056                new BroadcastReceiver() {
1057                    @Override
1058                    public void onReceive(Context context, Intent intent) {
1059                        String action = intent.getAction();
1060
1061                        if (action.equals(Intent.ACTION_SCREEN_ON)) {
1062                            sendMessage(CMD_SCREEN_STATE_CHANGED, 1);
1063                        } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1064                            sendMessage(CMD_SCREEN_STATE_CHANGED, 0);
1065                        } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
1066                            startNextBatchedScanAsync();
1067                        }
1068                    }
1069                }, filter);
1070
1071        mContext.registerReceiver(
1072                new BroadcastReceiver() {
1073                    @Override
1074                    public void onReceive(Context context, Intent intent) {
1075                       int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
1076                       sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
1077                    }
1078                },
1079                new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
1080
1081        mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
1082                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED), false,
1083                new ContentObserver(getHandler()) {
1084                    @Override
1085                    public void onChange(boolean selfChange) {
1086                        mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(),
1087                                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
1088                    }
1089                });
1090
1091        mContext.registerReceiver(
1092                new BroadcastReceiver() {
1093                    @Override
1094                    public void onReceive(Context context, Intent intent) {
1095                        sendMessage(CMD_BOOT_COMPLETED);
1096                    }
1097                },
1098                new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
1099
1100        mScanResultCache = new LruCache<>(SCAN_RESULT_CACHE_SIZE);
1101
1102        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
1103        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getName());
1104
1105        mSuspendWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WifiSuspend");
1106        mSuspendWakeLock.setReferenceCounted(false);
1107
1108        mTcpBufferSizes = mContext.getResources().getString(
1109                com.android.internal.R.string.config_wifi_tcp_buffers);
1110
1111        addState(mDefaultState);
1112            addState(mInitialState, mDefaultState);
1113            addState(mSupplicantStartingState, mDefaultState);
1114            addState(mSupplicantStartedState, mDefaultState);
1115                addState(mDriverStartingState, mSupplicantStartedState);
1116                addState(mDriverStartedState, mSupplicantStartedState);
1117                    addState(mScanModeState, mDriverStartedState);
1118                    addState(mConnectModeState, mDriverStartedState);
1119                        addState(mL2ConnectedState, mConnectModeState);
1120                            addState(mObtainingIpState, mL2ConnectedState);
1121                            addState(mVerifyingLinkState, mL2ConnectedState);
1122                            addState(mConnectedState, mL2ConnectedState);
1123                            addState(mRoamingState, mL2ConnectedState);
1124                        addState(mDisconnectingState, mConnectModeState);
1125                        addState(mDisconnectedState, mConnectModeState);
1126                        addState(mWpsRunningState, mConnectModeState);
1127                addState(mWaitForP2pDisableState, mSupplicantStartedState);
1128                addState(mDriverStoppingState, mSupplicantStartedState);
1129                addState(mDriverStoppedState, mSupplicantStartedState);
1130            addState(mSupplicantStoppingState, mDefaultState);
1131            addState(mSoftApStartingState, mDefaultState);
1132            addState(mSoftApStartedState, mDefaultState);
1133                addState(mTetheringState, mSoftApStartedState);
1134                addState(mTetheredState, mSoftApStartedState);
1135                addState(mUntetheringState, mSoftApStartedState);
1136
1137        setInitialState(mInitialState);
1138
1139        setLogRecSize(ActivityManager.isLowRamDeviceStatic() ? 100 : 3000);
1140        setLogOnlyTransitions(false);
1141        if (VDBG) setDbg(true);
1142
1143        //start the state machine
1144        start();
1145
1146        final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
1147        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1148        intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
1149        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1150    }
1151
1152
1153    PendingIntent getPrivateBroadcast(String action, int requestCode) {
1154        Intent intent = new Intent(action, null);
1155        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1156        intent.setPackage(this.getClass().getPackage().getName());
1157        return PendingIntent.getBroadcast(mContext, requestCode, intent, 0);
1158    }
1159
1160    private int mVerboseLoggingLevel = 0;
1161
1162    int getVerboseLoggingLevel() {
1163        return mVerboseLoggingLevel;
1164    }
1165
1166    void enableVerboseLogging(int verbose) {
1167        mVerboseLoggingLevel = verbose;
1168        if (verbose > 0) {
1169            DBG = true;
1170            VDBG = true;
1171            PDBG = true;
1172            mLogMessages = true;
1173            mWifiNative.setSupplicantLogLevel("DEBUG");
1174        } else {
1175            DBG = false;
1176            VDBG = false;
1177            PDBG = false;
1178            mLogMessages = false;
1179            mWifiNative.setSupplicantLogLevel("INFO");
1180        }
1181        mWifiAutoJoinController.enableVerboseLogging(verbose);
1182        mWifiMonitor.enableVerboseLogging(verbose);
1183        mWifiNative.enableVerboseLogging(verbose);
1184        mWifiConfigStore.enableVerboseLogging(verbose);
1185        mSupplicantStateTracker.enableVerboseLogging(verbose);
1186    }
1187
1188    private int mAggressiveHandover = 0;
1189
1190    int getAggressiveHandover() {
1191        return mAggressiveHandover;
1192    }
1193
1194    void enableAggressiveHandover(int enabled) {
1195        mAggressiveHandover = enabled;
1196    }
1197
1198    public void setAllowScansWithTraffic(int enabled) {
1199        mWifiConfigStore.alwaysEnableScansWhileAssociated.set(enabled);
1200    }
1201
1202    public int getAllowScansWithTraffic() {
1203        return mWifiConfigStore.alwaysEnableScansWhileAssociated.get();
1204    }
1205
1206    public void setAllowScansWhileAssociated(boolean enabled) {
1207        mWifiConfigStore.enableAutoJoinScanWhenAssociated.set(enabled);
1208    }
1209
1210    public boolean getAllowScansWhileAssociated() {
1211        return mWifiConfigStore.enableAutoJoinScanWhenAssociated.get();
1212    }
1213
1214    /*
1215     *
1216     * Framework scan control
1217     */
1218
1219    private boolean mAlarmEnabled = false;
1220    /* This is set from the overlay config file or from a secure setting.
1221     * A value of 0 disables scanning in the framework.
1222     */
1223    private long mFrameworkScanIntervalMs = 10000;
1224
1225    private AtomicInteger mDelayedScanCounter = new AtomicInteger();
1226
1227    private void setScanAlarm(boolean enabled) {
1228        if (PDBG) {
1229            loge("setScanAlarm " + enabled
1230                    + " period " + mDefaultFrameworkScanIntervalMs
1231                    + " mBackgroundScanSupported " + mBackgroundScanSupported);
1232        }
1233        if (mBackgroundScanSupported == false) {
1234            // Scan alarm is only used for background scans if they are not
1235            // offloaded to the wifi chipset, hence enable the scan alarm
1236            // gicing us RTC_WAKEUP of backgroundScan is NOT supported
1237            enabled = true;
1238        }
1239
1240        if (enabled == mAlarmEnabled) return;
1241        if (enabled) {
1242            /* Set RTC_WAKEUP alarms if PNO is not supported - because no one is */
1243            /* going to wake up the host processor to look for access points */
1244            mAlarmManager.set(AlarmManager.RTC_WAKEUP,
1245                    System.currentTimeMillis() + mDefaultFrameworkScanIntervalMs,
1246                    mScanIntent);
1247            mAlarmEnabled = true;
1248        } else {
1249            mAlarmManager.cancel(mScanIntent);
1250            mAlarmEnabled = false;
1251        }
1252    }
1253
1254    private void cancelDelayedScan() {
1255        mDelayedScanCounter.incrementAndGet();
1256        loge("cancelDelayedScan -> " + mDelayedScanCounter);
1257    }
1258
1259    private boolean checkAndRestartDelayedScan(int counter, boolean restart, int milli,
1260                                   ScanSettings settings, WorkSource workSource) {
1261        if (counter != mDelayedScanCounter.get()) {
1262            return false;
1263        }
1264        if (restart)
1265            startDelayedScan(milli, settings, workSource);
1266        return true;
1267    }
1268
1269    private void startDelayedScan(int milli, ScanSettings settings, WorkSource workSource) {
1270        if (milli <= 0) return;
1271        /**
1272         * The cases where the scan alarm should be run are :
1273         * - DisconnectedState && screenOn => used delayed timer
1274         * - DisconnectedState && !screenOn && mBackgroundScanSupported => PNO
1275         * - DisconnectedState && !screenOn && !mBackgroundScanSupported => used RTC_WAKEUP Alarm
1276         * - ConnectedState && screenOn => used delayed timer
1277         */
1278
1279        mDelayedScanCounter.incrementAndGet();
1280        if (mScreenOn &&
1281                (getCurrentState() == mDisconnectedState
1282                || getCurrentState() == mConnectedState)) {
1283            Bundle bundle = new Bundle();
1284            bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
1285            bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
1286            bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
1287            sendMessageDelayed(CMD_START_SCAN, SCAN_ALARM_SOURCE,
1288                    mDelayedScanCounter.get(), bundle, milli);
1289            if (DBG) loge("startDelayedScan send -> " + mDelayedScanCounter + " milli " + milli);
1290        } else if (mBackgroundScanSupported == false
1291                && !mScreenOn && getCurrentState() == mDisconnectedState) {
1292            setScanAlarm(true);
1293            if (DBG) loge("startDelayedScan start scan alarm -> "
1294                    + mDelayedScanCounter + " milli " + milli);
1295        } else {
1296            if (DBG) loge("startDelayedScan unhandled -> "
1297                    + mDelayedScanCounter + " milli " + milli);
1298        }
1299    }
1300
1301    private boolean setRandomMacOui() {
1302        String oui = mContext.getResources().getString(
1303                R.string.config_wifi_random_mac_oui, GOOGLE_OUI);
1304        String[] ouiParts = oui.split("-");
1305        byte[] ouiBytes = new byte[3];
1306        ouiBytes[0] = (byte) (Integer.parseInt(ouiParts[0], 16) & 0xFF);
1307        ouiBytes[1] = (byte) (Integer.parseInt(ouiParts[1], 16) & 0xFF);
1308        ouiBytes[2] = (byte) (Integer.parseInt(ouiParts[2], 16) & 0xFF);
1309
1310        logd("Setting OUI to " + oui);
1311        return mWifiNative.setScanningMacOui(ouiBytes);
1312    }
1313
1314    /*********************************************************
1315     * Methods exposed for public use
1316     ********************************************************/
1317
1318    public Messenger getMessenger() {
1319        return new Messenger(getHandler());
1320    }
1321
1322    public WifiMonitor getWifiMonitor() {
1323        return mWifiMonitor;
1324    }
1325
1326    /**
1327     * TODO: doc
1328     */
1329    public boolean syncPingSupplicant(AsyncChannel channel) {
1330        Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
1331        boolean result = (resultMsg.arg1 != FAILURE);
1332        resultMsg.recycle();
1333        return result;
1334    }
1335
1336    public List<WifiChannel> syncGetChannelList(AsyncChannel channel) {
1337        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CAPABILITY_FREQ);
1338        List<WifiChannel> list = null;
1339        if (resultMsg.obj != null) {
1340            list = new ArrayList<WifiChannel>();
1341            String freqs = (String) resultMsg.obj;
1342            String[] lines = freqs.split("\n");
1343            for (String line : lines)
1344                if (line.contains("MHz")) {
1345                    // line format: " 52 = 5260 MHz (NO_IBSS) (DFS)"
1346                    WifiChannel c = new WifiChannel();
1347                    String[] prop = line.split(" ");
1348                    if (prop.length < 5) continue;
1349                    try {
1350                        c.channelNum = Integer.parseInt(prop[1]);
1351                        c.freqMHz = Integer.parseInt(prop[3]);
1352                    } catch (NumberFormatException e) { }
1353                    c.isDFS = line.contains("(DFS)");
1354                    list.add(c);
1355                } else if (line.contains("Mode[B] Channels:")) {
1356                    // B channels are the same as G channels, skipped
1357                    break;
1358                }
1359        }
1360        resultMsg.recycle();
1361        return (list != null && list.size() > 0) ? list : null;
1362    }
1363
1364    /**
1365     * When settings allowing making use of untrusted networks change, trigger a scan
1366     * so as to kick of autojoin.
1367     */
1368    public void startScanForUntrustedSettingChange() {
1369        startScan(SET_ALLOW_UNTRUSTED_SOURCE, 0, null, null);
1370    }
1371
1372    /**
1373     * Initiate a wifi scan. If workSource is not null, blame is given to it, otherwise blame is
1374     * given to callingUid.
1375     *
1376     * @param callingUid The uid initiating the wifi scan. Blame will be given here unless
1377     *                   workSource is specified.
1378     * @param workSource If not null, blame is given to workSource.
1379     * @param settings Scan settings, see {@link ScanSettings}.
1380     */
1381    public void startScan(int callingUid, int scanCounter,
1382                          ScanSettings settings, WorkSource workSource) {
1383        Bundle bundle = new Bundle();
1384        bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, settings);
1385        bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
1386        bundle.putLong(SCAN_REQUEST_TIME, System.currentTimeMillis());
1387        sendMessage(CMD_START_SCAN, callingUid, scanCounter, bundle);
1388    }
1389
1390    /**
1391     * start or stop batched scanning using the given settings
1392     */
1393    public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid, int csph,
1394            WorkSource workSource) {
1395        Bundle bundle = new Bundle();
1396        bundle.putParcelable(BATCHED_SETTING, settings);
1397        bundle.putParcelable(BATCHED_WORKSOURCE, workSource);
1398        sendMessage(CMD_SET_BATCHED_SCAN, callingUid, csph, bundle);
1399    }
1400
1401    public List<BatchedScanResult> syncGetBatchedScanResultsList() {
1402        synchronized (mBatchedScanResults) {
1403            List<BatchedScanResult> batchedScanList =
1404                    new ArrayList<BatchedScanResult>(mBatchedScanResults.size());
1405            for(BatchedScanResult result: mBatchedScanResults) {
1406                batchedScanList.add(new BatchedScanResult(result));
1407            }
1408            return batchedScanList;
1409        }
1410    }
1411
1412    public void requestBatchedScanPoll() {
1413        sendMessage(CMD_POLL_BATCHED_SCAN);
1414    }
1415
1416    private void startBatchedScan() {
1417        if (mBatchedScanSettings == null) return;
1418
1419        if (mDhcpActive) {
1420            if (DBG) log("not starting Batched Scans due to DHCP");
1421            return;
1422        }
1423
1424        // first grab any existing data
1425        retrieveBatchedScanData();
1426
1427        if (PDBG) loge("try  starting Batched Scans due to DHCP");
1428
1429
1430        mAlarmManager.cancel(mBatchedScanIntervalIntent);
1431
1432        String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings);
1433        try {
1434            mExpectedBatchedScans = Integer.parseInt(scansExpected);
1435            setNextBatchedAlarm(mExpectedBatchedScans);
1436            if (mExpectedBatchedScans > 0) noteBatchedScanStart();
1437        } catch (NumberFormatException e) {
1438            stopBatchedScan();
1439            loge("Exception parsing WifiNative.setBatchedScanSettings response " + e);
1440        }
1441    }
1442
1443    // called from BroadcastListener
1444    private void startNextBatchedScanAsync() {
1445        sendMessage(CMD_START_NEXT_BATCHED_SCAN);
1446    }
1447
1448    private void startNextBatchedScan() {
1449        // first grab any existing data
1450        retrieveBatchedScanData();
1451
1452        setNextBatchedAlarm(mExpectedBatchedScans);
1453    }
1454
1455    private void handleBatchedScanPollRequest() {
1456        if (DBG) {
1457            log("handleBatchedScanPoll Request - mBatchedScanMinPollTime=" +
1458                    mBatchedScanMinPollTime + " , mBatchedScanSettings=" +
1459                    mBatchedScanSettings);
1460        }
1461        // if there is no appropriate PollTime that's because we either aren't
1462        // batching or we've already set a time for a poll request
1463        if (mBatchedScanMinPollTime == 0) return;
1464        if (mBatchedScanSettings == null) return;
1465
1466        long now = System.currentTimeMillis();
1467
1468        if (now > mBatchedScanMinPollTime) {
1469            // do the poll and reset our timers
1470            startNextBatchedScan();
1471        } else {
1472            mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, mBatchedScanMinPollTime,
1473                    mBatchedScanIntervalIntent);
1474            mBatchedScanMinPollTime = 0;
1475        }
1476    }
1477
1478    // return true if new/different
1479    private boolean recordBatchedScanSettings(int responsibleUid, int csph, Bundle bundle) {
1480        BatchedScanSettings settings = bundle.getParcelable(BATCHED_SETTING);
1481        WorkSource responsibleWorkSource = bundle.getParcelable(BATCHED_WORKSOURCE);
1482
1483        if (DBG) {
1484            log("set batched scan to " + settings + " for uid=" + responsibleUid +
1485                    ", worksource=" + responsibleWorkSource);
1486        }
1487        if (settings != null) {
1488            if (settings.equals(mBatchedScanSettings)) return false;
1489        } else {
1490            if (mBatchedScanSettings == null) return false;
1491        }
1492        mBatchedScanSettings = settings;
1493        if (responsibleWorkSource == null) responsibleWorkSource = new WorkSource(responsibleUid);
1494        mBatchedScanWorkSource = responsibleWorkSource;
1495        mBatchedScanCsph = csph;
1496        return true;
1497    }
1498
1499    private void stopBatchedScan() {
1500        mAlarmManager.cancel(mBatchedScanIntervalIntent);
1501        retrieveBatchedScanData();
1502        mWifiNative.setBatchedScanSettings(null);
1503        noteBatchedScanStop();
1504    }
1505
1506    private void setNextBatchedAlarm(int scansExpected) {
1507
1508        if (mBatchedScanSettings == null || scansExpected < 1) return;
1509
1510        mBatchedScanMinPollTime = System.currentTimeMillis() +
1511                mBatchedScanSettings.scanIntervalSec * 1000;
1512
1513        if (mBatchedScanSettings.maxScansPerBatch < scansExpected) {
1514            scansExpected = mBatchedScanSettings.maxScansPerBatch;
1515        }
1516
1517        int secToFull = mBatchedScanSettings.scanIntervalSec;
1518        secToFull *= scansExpected;
1519
1520        int debugPeriod = SystemProperties.getInt("wifi.batchedScan.pollPeriod", 0);
1521        if (debugPeriod > 0) secToFull = debugPeriod;
1522
1523        // set the alarm to do the next poll.  We set it a little short as we'd rather
1524        // wake up wearly than miss a scan due to buffer overflow
1525        mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
1526                + ((secToFull - (mBatchedScanSettings.scanIntervalSec / 2)) * 1000),
1527                mBatchedScanIntervalIntent);
1528    }
1529
1530    /**
1531     * Start reading new scan data
1532     * Data comes in as:
1533     * "scancount=5\n"
1534     * "nextcount=5\n"
1535     *   "apcount=3\n"
1536     *   "trunc\n" (optional)
1537     *     "bssid=...\n"
1538     *     "ssid=...\n"
1539     *     "freq=...\n" (in Mhz)
1540     *     "level=...\n"
1541     *     "dist=...\n" (in cm)
1542     *     "distsd=...\n" (standard deviation, in cm)
1543     *     "===="
1544     *     "bssid=...\n"
1545     *     etc
1546     *     "===="
1547     *     "bssid=...\n"
1548     *     etc
1549     *     "%%%%"
1550     *   "apcount=2\n"
1551     *     "bssid=...\n"
1552     *     etc
1553     *     "%%%%
1554     *   etc
1555     *   "----"
1556     */
1557    private final static boolean DEBUG_PARSE = false;
1558    private void retrieveBatchedScanData() {
1559        String rawData = mWifiNative.getBatchedScanResults();
1560        if (DEBUG_PARSE) log("rawData = " + rawData);
1561        mBatchedScanMinPollTime = 0;
1562        if (rawData == null || rawData.equalsIgnoreCase("OK")) {
1563            loge("Unexpected BatchedScanResults :" + rawData);
1564            return;
1565        }
1566
1567        int scanCount = 0;
1568        final String END_OF_BATCHES = "----";
1569        final String SCANCOUNT = "scancount=";
1570        final String TRUNCATED = "trunc";
1571        final String AGE = "age=";
1572        final String DIST = "dist=";
1573        final String DISTSD = "distSd=";
1574
1575        String splitData[] = rawData.split("\n");
1576        int n = 0;
1577        if (splitData[n].startsWith(SCANCOUNT)) {
1578            try {
1579                scanCount = Integer.parseInt(splitData[n++].substring(SCANCOUNT.length()));
1580            } catch (NumberFormatException e) {
1581                loge("scancount parseInt Exception from " + splitData[n]);
1582            }
1583        } else log("scancount not found");
1584        if (scanCount == 0) {
1585            loge("scanCount==0 - aborting");
1586            return;
1587        }
1588
1589        final Intent intent = new Intent(WifiManager.BATCHED_SCAN_RESULTS_AVAILABLE_ACTION);
1590        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1591
1592        synchronized (mBatchedScanResults) {
1593            mBatchedScanResults.clear();
1594            BatchedScanResult batchedScanResult = new BatchedScanResult();
1595
1596            String bssid = null;
1597            WifiSsid wifiSsid = null;
1598            int level = 0;
1599            int freq = 0;
1600            int dist, distSd;
1601            long tsf = 0;
1602            dist = distSd = ScanResult.UNSPECIFIED;
1603            final long now = SystemClock.elapsedRealtime();
1604            final int bssidStrLen = BSSID_STR.length();
1605
1606            while (true) {
1607                while (n < splitData.length) {
1608                    if (DEBUG_PARSE) logd("parsing " + splitData[n]);
1609                    if (splitData[n].equals(END_OF_BATCHES)) {
1610                        if (n+1 != splitData.length) {
1611                            loge("didn't consume " + (splitData.length-n));
1612                        }
1613                        if (mBatchedScanResults.size() > 0) {
1614                            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1615                        }
1616                        logd("retrieveBatchedScanResults X");
1617                        return;
1618                    }
1619                    if ((splitData[n].equals(END_STR)) || splitData[n].equals(DELIMITER_STR)) {
1620                        if (bssid != null) {
1621                            batchedScanResult.scanResults.add(new ScanResult(
1622                                    wifiSsid, bssid, null,
1623                                    level, freq, tsf, dist, distSd));
1624                            wifiSsid = null;
1625                            bssid = null;
1626                            level = 0;
1627                            freq = 0;
1628                            tsf = 0;
1629                            dist = distSd = ScanResult.UNSPECIFIED;
1630                        }
1631                        if (splitData[n].equals(END_STR)) {
1632                            if (batchedScanResult.scanResults.size() != 0) {
1633                                mBatchedScanResults.add(batchedScanResult);
1634                                batchedScanResult = new BatchedScanResult();
1635                            } else {
1636                                logd("Found empty batch");
1637                            }
1638                        }
1639                    } else if (splitData[n].equals(TRUNCATED)) {
1640                        batchedScanResult.truncated = true;
1641                    } else if (splitData[n].startsWith(BSSID_STR)) {
1642                        bssid = new String(splitData[n].getBytes(), bssidStrLen,
1643                                splitData[n].length() - bssidStrLen);
1644                    } else if (splitData[n].startsWith(FREQ_STR)) {
1645                        try {
1646                            freq = Integer.parseInt(splitData[n].substring(FREQ_STR.length()));
1647                        } catch (NumberFormatException e) {
1648                            loge("Invalid freqency: " + splitData[n]);
1649                            freq = 0;
1650                        }
1651                    } else if (splitData[n].startsWith(AGE)) {
1652                        try {
1653                            tsf = now - Long.parseLong(splitData[n].substring(AGE.length()));
1654                            tsf *= 1000; // convert mS -> uS
1655                        } catch (NumberFormatException e) {
1656                            loge("Invalid timestamp: " + splitData[n]);
1657                            tsf = 0;
1658                        }
1659                    } else if (splitData[n].startsWith(SSID_STR)) {
1660                        wifiSsid = WifiSsid.createFromAsciiEncoded(
1661                                splitData[n].substring(SSID_STR.length()));
1662                    } else if (splitData[n].startsWith(LEVEL_STR)) {
1663                        try {
1664                            level = Integer.parseInt(splitData[n].substring(LEVEL_STR.length()));
1665                            if (level > 0) level -= 256;
1666                        } catch (NumberFormatException e) {
1667                            loge("Invalid level: " + splitData[n]);
1668                            level = 0;
1669                        }
1670                    } else if (splitData[n].startsWith(DIST)) {
1671                        try {
1672                            dist = Integer.parseInt(splitData[n].substring(DIST.length()));
1673                        } catch (NumberFormatException e) {
1674                            loge("Invalid distance: " + splitData[n]);
1675                            dist = ScanResult.UNSPECIFIED;
1676                        }
1677                    } else if (splitData[n].startsWith(DISTSD)) {
1678                        try {
1679                            distSd = Integer.parseInt(splitData[n].substring(DISTSD.length()));
1680                        } catch (NumberFormatException e) {
1681                            loge("Invalid distanceSd: " + splitData[n]);
1682                            distSd = ScanResult.UNSPECIFIED;
1683                        }
1684                    } else {
1685                        loge("Unable to parse batched scan result line: " + splitData[n]);
1686                    }
1687                    n++;
1688                }
1689                rawData = mWifiNative.getBatchedScanResults();
1690                if (DEBUG_PARSE) log("reading more data:\n" + rawData);
1691                if (rawData == null) {
1692                    loge("Unexpected null BatchedScanResults");
1693                    return;
1694                }
1695                splitData = rawData.split("\n");
1696                if (splitData.length == 0 || splitData[0].equals("ok")) {
1697                    loge("batch scan results just ended!");
1698                    if (mBatchedScanResults.size() > 0) {
1699                        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1700                    }
1701                    return;
1702                }
1703                n = 0;
1704            }
1705        }
1706    }
1707
1708    private long mDisconnectedTimeStamp = 0;
1709
1710    public long getDisconnectedTimeMilli() {
1711        if (getCurrentState() == mDisconnectedState
1712                && mDisconnectedTimeStamp != 0) {
1713            long now_ms = System.currentTimeMillis();
1714            return now_ms - mDisconnectedTimeStamp;
1715        }
1716        return 0;
1717    }
1718
1719    // Keeping track of scan requests
1720    private long lastStartScanTimeStamp = 0;
1721    private long lastScanDuration = 0;
1722    // Last connect attempt is used to prevent scan requests:
1723    //  - for a period of 10 seconds after attempting to connect
1724    private long lastConnectAttempt = 0;
1725    private String lastScanFreqs = null;
1726
1727    // For debugging, keep track of last message status handling
1728    // TODO, find an equivalent mechanism as part of parent class
1729    private static int MESSAGE_HANDLING_STATUS_PROCESSED = 2;
1730    private static int MESSAGE_HANDLING_STATUS_OK = 1;
1731    private static int MESSAGE_HANDLING_STATUS_UNKNOWN = 0;
1732    private static int MESSAGE_HANDLING_STATUS_REFUSED = -1;
1733    private static int MESSAGE_HANDLING_STATUS_FAIL = -2;
1734    private static int MESSAGE_HANDLING_STATUS_OBSOLETE = -3;
1735    private static int MESSAGE_HANDLING_STATUS_DEFERRED = -4;
1736    private static int MESSAGE_HANDLING_STATUS_DISCARD = -5;
1737    private static int MESSAGE_HANDLING_STATUS_LOOPED = -6;
1738    private static int MESSAGE_HANDLING_STATUS_HANDLING_ERROR = -7;
1739
1740    private int messageHandlingStatus = 0;
1741
1742    //TODO: this is used only to track connection attempts, however the link state and packet per
1743    //TODO: second logic should be folded into that
1744    private boolean checkOrDeferScanAllowed(Message msg) {
1745        long now = System.currentTimeMillis();
1746        if (lastConnectAttempt != 0 && (now - lastConnectAttempt) < 10000) {
1747            Message dmsg = Message.obtain(msg);
1748            sendMessageDelayed(dmsg, 11000 - (now - lastConnectAttempt));
1749            return false;
1750        }
1751        return true;
1752    }
1753
1754    private int mOnTime = 0;
1755    private int mTxTime = 0;
1756    private int mRxTime = 0;
1757    private int mOnTimeStartScan = 0;
1758    private int mTxTimeStartScan = 0;
1759    private int mRxTimeStartScan = 0;
1760    private int mOnTimeScan = 0;
1761    private int mTxTimeScan = 0;
1762    private int mRxTimeScan = 0;
1763    private int mOnTimeThisScan = 0;
1764    private int mTxTimeThisScan = 0;
1765    private int mRxTimeThisScan = 0;
1766
1767    private int mOnTimeScreenStateChange = 0;
1768    private int mOnTimeAtLastReport = 0;
1769    private long lastOntimeReportTimeStamp = 0;
1770    private long lastScreenStateChangeTimeStamp = 0;
1771    private int mOnTimeLastReport = 0;
1772    private int mTxTimeLastReport = 0;
1773    private int mRxTimeLastReport = 0;
1774
1775    private long lastLinkLayerStatsUpdate = 0;
1776
1777    String reportOnTime() {
1778        long now = System.currentTimeMillis();
1779        StringBuilder sb = new StringBuilder();
1780        // Report stats since last report
1781        int on = mOnTime - mOnTimeLastReport;
1782        mOnTimeLastReport = mOnTime;
1783        int tx = mTxTime - mTxTimeLastReport;
1784        mTxTimeLastReport = mTxTime;
1785        int rx = mRxTime - mRxTimeLastReport;
1786        mRxTimeLastReport = mRxTime;
1787        int period = (int)(now - lastOntimeReportTimeStamp);
1788        lastOntimeReportTimeStamp = now;
1789        sb.append(String.format("[on:%d tx:%d rx:%d period:%d]", on, tx, rx, period));
1790        // Report stats since Screen State Changed
1791        on = mOnTime - mOnTimeScreenStateChange;
1792        period = (int)(now - lastScreenStateChangeTimeStamp);
1793        sb.append(String.format(" from screen [on:%d period:%d]", on, period));
1794        return sb.toString();
1795    }
1796
1797    WifiLinkLayerStats getWifiLinkLayerStats(boolean dbg) {
1798        WifiLinkLayerStats stats = null;
1799        if (mWifiLinkLayerStatsSupported > 0) {
1800            String name = "wlan0";
1801            stats = mWifiNative.getWifiLinkLayerStats(name);
1802            if (name != null && stats == null && mWifiLinkLayerStatsSupported > 0) {
1803                mWifiLinkLayerStatsSupported -= 1;
1804            } else if (stats != null) {
1805                lastLinkLayerStatsUpdate = System.currentTimeMillis();
1806                mOnTime = stats.on_time;
1807                mTxTime = stats.tx_time;
1808                mRxTime = stats.rx_time;
1809                mRunningBeaconCount = stats.beacon_rx;
1810                if (dbg) {
1811                    loge(stats.toString());
1812                }
1813            }
1814        }
1815        if (stats == null || mWifiLinkLayerStatsSupported <= 0) {
1816            long mTxPkts = TrafficStats.getTxPackets(mInterfaceName);
1817            long mRxPkts = TrafficStats.getRxPackets(mInterfaceName);
1818            mWifiInfo.updatePacketRates(mTxPkts, mRxPkts);
1819        } else {
1820            mWifiInfo.updatePacketRates(stats);
1821        }
1822        return stats;
1823    }
1824
1825    void startRadioScanStats() {
1826        WifiLinkLayerStats stats = getWifiLinkLayerStats(false);
1827        if (stats != null) {
1828            mOnTimeStartScan = stats.on_time;
1829            mTxTimeStartScan = stats.tx_time;
1830            mRxTimeStartScan = stats.rx_time;
1831            mOnTime = stats.on_time;
1832            mTxTime = stats.tx_time;
1833            mRxTime = stats.rx_time;
1834        }
1835    }
1836
1837    void closeRadioScanStats() {
1838        WifiLinkLayerStats stats = getWifiLinkLayerStats(false);
1839        if (stats != null) {
1840            mOnTimeThisScan = stats.on_time - mOnTimeStartScan;
1841            mTxTimeThisScan = stats.tx_time - mTxTimeStartScan;
1842            mRxTimeThisScan = stats.rx_time - mRxTimeStartScan;
1843            mOnTimeScan += mOnTimeThisScan;
1844            mTxTimeScan += mTxTimeThisScan;
1845            mRxTimeScan += mRxTimeThisScan;
1846        }
1847    }
1848
1849    // If workSource is not null, blame is given to it, otherwise blame is given to callingUid.
1850    private void noteScanStart(int callingUid, WorkSource workSource) {
1851        long now = System.currentTimeMillis();
1852        lastStartScanTimeStamp = now;
1853        lastScanDuration = 0;
1854        if (DBG) {
1855            String ts = String.format("[%,d ms]", now);
1856            if (workSource != null) {
1857                loge(ts + " noteScanStart" + workSource.toString()
1858                        + " uid " + Integer.toString(callingUid));
1859            } else {
1860                loge(ts + " noteScanstart no scan source"
1861                        + " uid " + Integer.toString(callingUid));
1862            }
1863        }
1864        startRadioScanStats();
1865        if (mScanWorkSource == null && ((callingUid != UNKNOWN_SCAN_SOURCE
1866                && callingUid != SCAN_ALARM_SOURCE)
1867                || workSource != null)) {
1868            mScanWorkSource = workSource != null ? workSource : new WorkSource(callingUid);
1869            try {
1870                mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
1871            } catch (RemoteException e) {
1872                log(e.toString());
1873            }
1874        }
1875    }
1876
1877    private void noteScanEnd() {
1878        long now = System.currentTimeMillis();
1879        if (lastStartScanTimeStamp != 0) {
1880            lastScanDuration = now - lastStartScanTimeStamp;
1881        }
1882        lastStartScanTimeStamp = 0;
1883        if (DBG) {
1884            String ts = String.format("[%,d ms]", now);
1885            if (mScanWorkSource != null)
1886                loge(ts + " noteScanEnd " + mScanWorkSource.toString()
1887                        + " onTime=" + mOnTimeThisScan);
1888            else
1889                loge(ts + " noteScanEnd no scan source"
1890                        + " onTime=" + mOnTimeThisScan);
1891        }
1892        if (mScanWorkSource != null) {
1893            try {
1894                mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
1895            } catch (RemoteException e) {
1896                log(e.toString());
1897            } finally {
1898                mScanWorkSource = null;
1899            }
1900        }
1901    }
1902
1903    private void noteBatchedScanStart() {
1904        if (PDBG) loge("noteBatchedScanstart()");
1905        // note the end of a previous scan set
1906        if (mNotedBatchedScanWorkSource != null &&
1907                (mNotedBatchedScanWorkSource.equals(mBatchedScanWorkSource) == false ||
1908                 mNotedBatchedScanCsph != mBatchedScanCsph)) {
1909            try {
1910                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
1911            } catch (RemoteException e) {
1912                log(e.toString());
1913            } finally {
1914                mNotedBatchedScanWorkSource = null;
1915                mNotedBatchedScanCsph = 0;
1916            }
1917        }
1918        // note the start of the new
1919        try {
1920            mBatteryStats.noteWifiBatchedScanStartedFromSource(mBatchedScanWorkSource,
1921                    mBatchedScanCsph);
1922            mNotedBatchedScanWorkSource = mBatchedScanWorkSource;
1923            mNotedBatchedScanCsph = mBatchedScanCsph;
1924        } catch (RemoteException e) {
1925            log(e.toString());
1926        }
1927    }
1928
1929    private void noteBatchedScanStop() {
1930        if (PDBG) loge("noteBatchedScanstop()");
1931
1932        if (mNotedBatchedScanWorkSource != null) {
1933            try {
1934                mBatteryStats.noteWifiBatchedScanStoppedFromSource(mNotedBatchedScanWorkSource);
1935            } catch (RemoteException e) {
1936                log(e.toString());
1937            } finally {
1938                mNotedBatchedScanWorkSource = null;
1939                mNotedBatchedScanCsph = 0;
1940            }
1941        }
1942    }
1943
1944    private void handleScanRequest(int type, Message message) {
1945        ScanSettings settings = null;
1946        WorkSource workSource = null;
1947
1948        // unbundle parameters
1949        Bundle bundle = (Bundle) message.obj;
1950
1951        if (bundle != null) {
1952            settings = bundle.getParcelable(CUSTOMIZED_SCAN_SETTING);
1953            workSource = bundle.getParcelable(CUSTOMIZED_SCAN_WORKSOURCE);
1954        }
1955
1956        // parse scan settings
1957        String freqs = null;
1958        if (settings != null && settings.channelSet != null) {
1959            StringBuilder sb = new StringBuilder();
1960            boolean first = true;
1961            for (WifiChannel channel : settings.channelSet) {
1962                if (!first) sb.append(','); else first = false;
1963                sb.append(channel.freqMHz);
1964            }
1965            freqs = sb.toString();
1966        }
1967
1968        // call wifi native to start the scan
1969        if (startScanNative(type, freqs)) {
1970            // only count battery consumption if scan request is accepted
1971            noteScanStart(message.arg1, workSource);
1972            // a full scan covers everything, clearing scan request buffer
1973            if (freqs == null)
1974                mBufferedScanMsg.clear();
1975            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
1976            if (workSource != null) {
1977                // External worksource was passed along the scan request,
1978                // hence always send a broadcast
1979                mSendScanResultsBroadcast = true;
1980            }
1981            return;
1982        }
1983
1984        // if reach here, scan request is rejected
1985
1986        if (!mIsScanOngoing) {
1987            // if rejection is NOT due to ongoing scan (e.g. bad scan parameters),
1988
1989            // discard this request and pop up the next one
1990            if (mBufferedScanMsg.size() > 0) {
1991                sendMessage(mBufferedScanMsg.remove());
1992            }
1993            messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
1994        } else if (!mIsFullScanOngoing) {
1995            // if rejection is due to an ongoing scan, and the ongoing one is NOT a full scan,
1996            // buffer the scan request to make sure specified channels will be scanned eventually
1997            if (freqs == null)
1998                mBufferedScanMsg.clear();
1999            if (mBufferedScanMsg.size() < SCAN_REQUEST_BUFFER_MAX_SIZE) {
2000                Message msg = obtainMessage(CMD_START_SCAN,
2001                        message.arg1, message.arg2, bundle);
2002                mBufferedScanMsg.add(msg);
2003            } else {
2004                // if too many requests in buffer, combine them into a single full scan
2005                bundle = new Bundle();
2006                bundle.putParcelable(CUSTOMIZED_SCAN_SETTING, null);
2007                bundle.putParcelable(CUSTOMIZED_SCAN_WORKSOURCE, workSource);
2008                Message msg = obtainMessage(CMD_START_SCAN, message.arg1, message.arg2, bundle);
2009                mBufferedScanMsg.clear();
2010                mBufferedScanMsg.add(msg);
2011            }
2012            messageHandlingStatus = MESSAGE_HANDLING_STATUS_LOOPED;
2013        } else {
2014            // mIsScanOngoing and mIsFullScanOngoing
2015            messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
2016        }
2017    }
2018
2019
2020    /** return true iff scan request is accepted */
2021    private boolean startScanNative(int type, String freqs) {
2022        if (mWifiNative.scan(type, freqs)) {
2023            mIsScanOngoing = true;
2024            mIsFullScanOngoing = (freqs == null);
2025            lastScanFreqs = freqs;
2026            return true;
2027        }
2028        return false;
2029    }
2030
2031    /**
2032     * TODO: doc
2033     */
2034    public void setSupplicantRunning(boolean enable) {
2035        if (enable) {
2036            sendMessage(CMD_START_SUPPLICANT);
2037        } else {
2038            sendMessage(CMD_STOP_SUPPLICANT);
2039        }
2040    }
2041
2042    /**
2043     * TODO: doc
2044     */
2045    public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
2046        if (enable) {
2047            sendMessage(CMD_START_AP, wifiConfig);
2048        } else {
2049            sendMessage(CMD_STOP_AP);
2050        }
2051    }
2052
2053    public void setWifiApConfiguration(WifiConfiguration config) {
2054        mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
2055    }
2056
2057    public WifiConfiguration syncGetWifiApConfiguration() {
2058        Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG);
2059        WifiConfiguration ret = (WifiConfiguration) resultMsg.obj;
2060        resultMsg.recycle();
2061        return ret;
2062    }
2063
2064    /**
2065     * TODO: doc
2066     */
2067    public int syncGetWifiState() {
2068        return mWifiState.get();
2069    }
2070
2071    /**
2072     * TODO: doc
2073     */
2074    public String syncGetWifiStateByName() {
2075        switch (mWifiState.get()) {
2076            case WIFI_STATE_DISABLING:
2077                return "disabling";
2078            case WIFI_STATE_DISABLED:
2079                return "disabled";
2080            case WIFI_STATE_ENABLING:
2081                return "enabling";
2082            case WIFI_STATE_ENABLED:
2083                return "enabled";
2084            case WIFI_STATE_UNKNOWN:
2085                return "unknown state";
2086            default:
2087                return "[invalid state]";
2088        }
2089    }
2090
2091    /**
2092     * TODO: doc
2093     */
2094    public int syncGetWifiApState() {
2095        return mWifiApState.get();
2096    }
2097
2098    /**
2099     * TODO: doc
2100     */
2101    public String syncGetWifiApStateByName() {
2102        switch (mWifiApState.get()) {
2103            case WIFI_AP_STATE_DISABLING:
2104                return "disabling";
2105            case WIFI_AP_STATE_DISABLED:
2106                return "disabled";
2107            case WIFI_AP_STATE_ENABLING:
2108                return "enabling";
2109            case WIFI_AP_STATE_ENABLED:
2110                return "enabled";
2111            case WIFI_AP_STATE_FAILED:
2112                return "failed";
2113            default:
2114                return "[invalid state]";
2115        }
2116    }
2117
2118    /**
2119     * Get status information for the current connection, if any.
2120     * @return a {@link WifiInfo} object containing information about the current connection
2121     *
2122     */
2123    public WifiInfo syncRequestConnectionInfo() {
2124        return mWifiInfo;
2125    }
2126
2127    public DhcpResults syncGetDhcpResults() {
2128        synchronized (mDhcpResultsLock) {
2129            return new DhcpResults(mDhcpResults);
2130        }
2131    }
2132
2133    /**
2134     * TODO: doc
2135     */
2136    public void setDriverStart(boolean enable) {
2137        if (enable) {
2138            sendMessage(CMD_START_DRIVER);
2139        } else {
2140            sendMessage(CMD_STOP_DRIVER);
2141        }
2142    }
2143
2144    /**
2145     * TODO: doc
2146     */
2147    public void setOperationalMode(int mode) {
2148        if (DBG) log("setting operational mode to " + String.valueOf(mode));
2149        sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
2150    }
2151
2152    /**
2153     * TODO: doc
2154     */
2155    public List<ScanResult> syncGetScanResultsList() {
2156        synchronized (mScanResultCache) {
2157            List<ScanResult> scanList = new ArrayList<ScanResult>();
2158            for(ScanDetail result: mScanResults) {
2159                scanList.add(new ScanResult(result.getScanResult()));
2160            }
2161            return scanList;
2162        }
2163    }
2164
2165    public void disableEphemeralNetwork(String SSID) {
2166        if (SSID != null) {
2167            sendMessage(CMD_DISABLE_EPHEMERAL_NETWORK, SSID);
2168        }
2169    }
2170
2171    /**
2172     * Get unsynchronized pointer to scan result list
2173     * Can be called only from AutoJoinController which runs in the WifiStateMachine context
2174     */
2175    public List<ScanDetail> getScanResultsListNoCopyUnsync() {
2176        return mScanResults;
2177    }
2178
2179    /**
2180     * Disconnect from Access Point
2181     */
2182    public void disconnectCommand() {
2183        sendMessage(CMD_DISCONNECT);
2184    }
2185
2186    public void disconnectCommand(int uid, int reason) {
2187        sendMessage(CMD_DISCONNECT, uid, reason);
2188    }
2189
2190    /**
2191     * Initiate a reconnection to AP
2192     */
2193    public void reconnectCommand() {
2194        sendMessage(CMD_RECONNECT);
2195    }
2196
2197    /**
2198     * Initiate a re-association to AP
2199     */
2200    public void reassociateCommand() {
2201        sendMessage(CMD_REASSOCIATE);
2202    }
2203
2204    /**
2205     * Reload networks and then reconnect; helps load correct data for TLS networks
2206     */
2207
2208    public void reloadTlsNetworksAndReconnect() {
2209        sendMessage(CMD_RELOAD_TLS_AND_RECONNECT);
2210    }
2211
2212    /**
2213     * Add a network synchronously
2214     *
2215     * @return network id of the new network
2216     */
2217    public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
2218        Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
2219        int result = resultMsg.arg1;
2220        resultMsg.recycle();
2221        return result;
2222    }
2223
2224    /**
2225     * Get configured networks synchronously
2226     * @param channel
2227     * @return
2228     */
2229
2230    public List<WifiConfiguration> syncGetConfiguredNetworks(int uuid, AsyncChannel channel) {
2231        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS, uuid);
2232        List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
2233        resultMsg.recycle();
2234        return result;
2235    }
2236
2237    public List<WifiConfiguration> syncGetPrivilegedConfiguredNetwork(AsyncChannel channel) {
2238        Message resultMsg = channel.sendMessageSynchronously(
2239                CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS);
2240        List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
2241        resultMsg.recycle();
2242        return result;
2243    }
2244
2245    public WifiConfiguration syncGetMatchingWifiConfig(ScanResult scanResult, AsyncChannel channel) {
2246        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_MATCHING_CONFIG, scanResult);
2247        return (WifiConfiguration) resultMsg.obj;
2248    }
2249
2250    /**
2251     * Get connection statistics synchronously
2252     * @param channel
2253     * @return
2254     */
2255
2256    public WifiConnectionStatistics syncGetConnectionStatistics(AsyncChannel channel) {
2257        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONNECTION_STATISTICS);
2258        WifiConnectionStatistics result = (WifiConnectionStatistics) resultMsg.obj;
2259        resultMsg.recycle();
2260        return result;
2261    }
2262
2263    /**
2264     * Get adaptors synchronously
2265     */
2266
2267    public int syncGetSupportedFeatures(AsyncChannel channel) {
2268        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_SUPPORTED_FEATURES);
2269        int supportedFeatureSet = resultMsg.arg1;
2270        resultMsg.recycle();
2271        return supportedFeatureSet;
2272    }
2273
2274    /**
2275     * Get link layers stats for adapter synchronously
2276     */
2277    public WifiLinkLayerStats syncGetLinkLayerStats(AsyncChannel channel) {
2278        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_LINK_LAYER_STATS);
2279        WifiLinkLayerStats result = (WifiLinkLayerStats) resultMsg.obj;
2280        resultMsg.recycle();
2281        return result;
2282    }
2283
2284    /**
2285     * Delete a network
2286     *
2287     * @param networkId id of the network to be removed
2288     */
2289    public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
2290        Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
2291        boolean result = (resultMsg.arg1 != FAILURE);
2292        resultMsg.recycle();
2293        return result;
2294    }
2295
2296    /**
2297     * Enable a network
2298     *
2299     * @param netId network id of the network
2300     * @param disableOthers true, if all other networks have to be disabled
2301     * @return {@code true} if the operation succeeds, {@code false} otherwise
2302     */
2303    public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
2304        Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
2305                disableOthers ? 1 : 0);
2306        boolean result = (resultMsg.arg1 != FAILURE);
2307        resultMsg.recycle();
2308        return result;
2309    }
2310
2311    /**
2312     * Disable a network
2313     *
2314     * @param netId network id of the network
2315     * @return {@code true} if the operation succeeds, {@code false} otherwise
2316     */
2317    public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
2318        Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
2319        boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
2320        resultMsg.recycle();
2321        return result;
2322    }
2323
2324    /**
2325     * Retrieves a WPS-NFC configuration token for the specified network
2326     * @return a hex string representation of the WPS-NFC configuration token
2327     */
2328    public String syncGetWpsNfcConfigurationToken(int netId) {
2329        return mWifiNative.getNfcWpsConfigurationToken(netId);
2330    }
2331
2332    void enableBackgroundScan(boolean enable) {
2333        if (enable) {
2334            mWifiConfigStore.enableAllNetworks();
2335        }
2336        mWifiNative.enableBackgroundScan(enable);
2337    }
2338
2339    /**
2340     * Blacklist a BSSID. This will avoid the AP if there are
2341     * alternate APs to connect
2342     *
2343     * @param bssid BSSID of the network
2344     */
2345    public void addToBlacklist(String bssid) {
2346        sendMessage(CMD_BLACKLIST_NETWORK, bssid);
2347    }
2348
2349    /**
2350     * Clear the blacklist list
2351     *
2352     */
2353    public void clearBlacklist() {
2354        sendMessage(CMD_CLEAR_BLACKLIST);
2355    }
2356
2357    public void enableRssiPolling(boolean enabled) {
2358       sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
2359    }
2360
2361    public void enableAllNetworks() {
2362        sendMessage(CMD_ENABLE_ALL_NETWORKS);
2363    }
2364
2365    /**
2366     * Start filtering Multicast v4 packets
2367     */
2368    public void startFilteringMulticastV4Packets() {
2369        mFilteringMulticastV4Packets.set(true);
2370        sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0);
2371    }
2372
2373    /**
2374     * Stop filtering Multicast v4 packets
2375     */
2376    public void stopFilteringMulticastV4Packets() {
2377        mFilteringMulticastV4Packets.set(false);
2378        sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0);
2379    }
2380
2381    /**
2382     * Start filtering Multicast v4 packets
2383     */
2384    public void startFilteringMulticastV6Packets() {
2385        sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0);
2386    }
2387
2388    /**
2389     * Stop filtering Multicast v4 packets
2390     */
2391    public void stopFilteringMulticastV6Packets() {
2392        sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0);
2393    }
2394
2395    /**
2396     * Set high performance mode of operation.
2397     * Enabling would set active power mode and disable suspend optimizations;
2398     * disabling would set auto power mode and enable suspend optimizations
2399     * @param enable true if enable, false otherwise
2400     */
2401    public void setHighPerfModeEnabled(boolean enable) {
2402        sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0);
2403    }
2404
2405    /**
2406     * Set the country code
2407     * @param countryCode following ISO 3166 format
2408     * @param persist {@code true} if the setting should be remembered.
2409     */
2410    public synchronized void setCountryCode(String countryCode, boolean persist) {
2411        // If it's a good country code, apply after the current
2412        // wifi connection is terminated; ignore resetting of code
2413        // for now (it is unclear what the chipset should do when
2414        // country code is reset)
2415
2416        if (TextUtils.isEmpty(countryCode)) {
2417            log("Ignoring resetting of country code");
2418        } else {
2419            // if mCountryCodeSequence == 0, it is the first time to set country code, always set
2420            // else only when the new country code is different from the current one to set
2421            int countryCodeSequence = mCountryCodeSequence.get();
2422            if(countryCodeSequence == 0 || countryCode.equals(mSetCountryCode) == false) {
2423
2424                countryCodeSequence = mCountryCodeSequence.incrementAndGet();
2425                mSetCountryCode = countryCode;
2426                sendMessage(CMD_SET_COUNTRY_CODE, countryCodeSequence, persist ? 1 : 0,
2427                        countryCode);
2428            }
2429
2430            if (persist) {
2431                Settings.Global.putString(mContext.getContentResolver(),
2432                        Settings.Global.WIFI_COUNTRY_CODE,
2433                        countryCode);
2434            }
2435        }
2436    }
2437
2438    /**
2439     * Get the country code
2440     * @param countryCode following ISO 3166 format
2441     */
2442    public String getCountryCode() {
2443        return mSetCountryCode;
2444    }
2445
2446
2447    /**
2448     * Set the operational frequency band
2449     * @param band
2450     * @param persist {@code true} if the setting should be remembered.
2451     */
2452    public void setFrequencyBand(int band, boolean persist) {
2453        if (persist) {
2454            Settings.Global.putInt(mContext.getContentResolver(),
2455                    Settings.Global.WIFI_FREQUENCY_BAND,
2456                    band);
2457        }
2458        sendMessage(CMD_SET_FREQUENCY_BAND, band, 0);
2459    }
2460
2461    /**
2462     * Enable TDLS for a specific MAC address
2463     */
2464    public void enableTdls(String remoteMacAddress, boolean enable) {
2465        int enabler = enable ? 1 : 0;
2466        sendMessage(CMD_ENABLE_TDLS, enabler, 0, remoteMacAddress);
2467    }
2468
2469    /**
2470     * Returns the operational frequency band
2471     */
2472    public int getFrequencyBand() {
2473        return mFrequencyBand.get();
2474    }
2475
2476    /**
2477     * Returns the wifi configuration file
2478     */
2479    public String getConfigFile() {
2480        return mWifiConfigStore.getConfigFile();
2481    }
2482
2483    /**
2484     * Send a message indicating bluetooth adapter connection state changed
2485     */
2486    public void sendBluetoothAdapterStateChange(int state) {
2487        sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0);
2488    }
2489
2490    /**
2491     * Send a message indicating a package has been uninstalled.
2492     */
2493    public void removeAppConfigs(String packageName, int uid) {
2494        // Build partial AppInfo manually - package may not exist in database any more
2495        ApplicationInfo ai = new ApplicationInfo();
2496        ai.packageName = packageName;
2497        ai.uid = uid;
2498        sendMessage(CMD_REMOVE_APP_CONFIGURATIONS, ai);
2499    }
2500
2501    /**
2502     * Send a message indicating a user has been removed.
2503     */
2504    public void removeUserConfigs(int userId) {
2505        sendMessage(CMD_REMOVE_USER_CONFIGURATIONS, userId);
2506    }
2507
2508    /**
2509     * Save configuration on supplicant
2510     *
2511     * @return {@code true} if the operation succeeds, {@code false} otherwise
2512     *
2513     * TODO: deprecate this
2514     */
2515    public boolean syncSaveConfig(AsyncChannel channel) {
2516        Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
2517        boolean result = (resultMsg.arg1 != FAILURE);
2518        resultMsg.recycle();
2519        return result;
2520    }
2521
2522    public void updateBatteryWorkSource(WorkSource newSource) {
2523        synchronized (mRunningWifiUids) {
2524            try {
2525                if (newSource != null) {
2526                    mRunningWifiUids.set(newSource);
2527                }
2528                if (mIsRunning) {
2529                    if (mReportedRunning) {
2530                        // If the work source has changed since last time, need
2531                        // to remove old work from battery stats.
2532                        if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
2533                            mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
2534                                    mRunningWifiUids);
2535                            mLastRunningWifiUids.set(mRunningWifiUids);
2536                        }
2537                    } else {
2538                        // Now being started, report it.
2539                        mBatteryStats.noteWifiRunning(mRunningWifiUids);
2540                        mLastRunningWifiUids.set(mRunningWifiUids);
2541                        mReportedRunning = true;
2542                    }
2543                } else {
2544                    if (mReportedRunning) {
2545                        // Last reported we were running, time to stop.
2546                        mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
2547                        mLastRunningWifiUids.clear();
2548                        mReportedRunning = false;
2549                    }
2550                }
2551                mWakeLock.setWorkSource(newSource);
2552            } catch (RemoteException ignore) {
2553            }
2554        }
2555    }
2556
2557    @Override
2558    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2559        super.dump(fd, pw, args);
2560        mSupplicantStateTracker.dump(fd, pw, args);
2561        pw.println("mLinkProperties " + mLinkProperties);
2562        pw.println("mWifiInfo " + mWifiInfo);
2563        pw.println("mDhcpResults " + mDhcpResults);
2564        pw.println("mNetworkInfo " + mNetworkInfo);
2565        pw.println("mLastSignalLevel " + mLastSignalLevel);
2566        pw.println("mLastBssid " + mLastBssid);
2567        pw.println("mLastNetworkId " + mLastNetworkId);
2568        pw.println("mOperationalMode " + mOperationalMode);
2569        pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
2570        pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
2571        pw.println("Supplicant status " + mWifiNative.status(true));
2572        pw.println("mEnableBackgroundScan " + mEnableBackgroundScan);
2573        pw.println("mSetCountryCode " + mSetCountryCode);
2574        pw.println("mDriverSetCountryCode " + mDriverSetCountryCode);
2575        mNetworkFactory.dump(fd, pw, args);
2576        mUntrustedNetworkFactory.dump(fd, pw, args);
2577        pw.println();
2578        mWifiConfigStore.dump(fd, pw, args);
2579    }
2580
2581    /*********************************************************
2582     * Internal private functions
2583     ********************************************************/
2584
2585    private void logStateAndMessage(Message message, String state) {
2586        messageHandlingStatus = 0;
2587        if (mLogMessages) {
2588            //long now = SystemClock.elapsedRealtimeNanos();
2589            //String ts = String.format("[%,d us]", now/1000);
2590
2591            loge( " " + state + " " + getLogRecString(message));
2592        }
2593    }
2594
2595    /**
2596     * helper, prints the milli time since boot wi and w/o suspended time
2597     */
2598    String printTime() {
2599        StringBuilder sb = new StringBuilder();
2600        sb.append(" rt=").append(SystemClock.uptimeMillis());
2601        sb.append("/").append(SystemClock.elapsedRealtime());
2602        return sb.toString();
2603    }
2604
2605    /**
2606     * Return the additional string to be logged by LogRec, default
2607     *
2608     * @param msg that was processed
2609     * @return information to be logged as a String
2610     */
2611    protected String getLogRecString(Message msg) {
2612        WifiConfiguration config;
2613        Long now;
2614        String report;
2615        String key;
2616        StringBuilder sb = new StringBuilder();
2617        if (mScreenOn) {
2618            sb.append("!");
2619        }
2620        if (messageHandlingStatus != MESSAGE_HANDLING_STATUS_UNKNOWN) {
2621            sb.append("(").append(messageHandlingStatus).append(")");
2622        }
2623        sb.append(smToString(msg));
2624        if (msg.sendingUid > 0 && msg.sendingUid != Process.WIFI_UID) {
2625            sb.append(" uid=" + msg.sendingUid);
2626        }
2627        switch (msg.what) {
2628            case CMD_START_SCAN:
2629                now = System.currentTimeMillis();
2630                sb.append(" ");
2631                sb.append(Integer.toString(msg.arg1));
2632                sb.append(" ");
2633                sb.append(Integer.toString(msg.arg2));
2634                sb.append(" ic=");
2635                sb.append(Integer.toString(sScanAlarmIntentCount));
2636                if (msg.obj != null) {
2637                    Bundle bundle = (Bundle)msg.obj;
2638                    Long request = bundle.getLong(SCAN_REQUEST_TIME, 0);
2639                    if (request != 0) {
2640                        sb.append(" proc(ms):").append(now - request);
2641                    }
2642                }
2643                if (mIsScanOngoing) sb.append(" onGoing");
2644                if (mIsFullScanOngoing) sb.append(" full");
2645                if (lastStartScanTimeStamp != 0) {
2646                    sb.append(" started:").append(lastStartScanTimeStamp);
2647                    sb.append(",").append(now - lastStartScanTimeStamp);
2648                }
2649                if (lastScanDuration != 0) {
2650                    sb.append(" dur:").append(lastScanDuration);
2651                }
2652                sb.append(" cnt=").append(mDelayedScanCounter);
2653                sb.append(" rssi=").append(mWifiInfo.getRssi());
2654                sb.append(" f=").append(mWifiInfo.getFrequency());
2655                sb.append(" sc=").append(mWifiInfo.score);
2656                sb.append(" link=").append(mWifiInfo.getLinkSpeed());
2657                sb.append(String.format(" tx=%.1f,", mWifiInfo.txSuccessRate));
2658                sb.append(String.format(" %.1f,", mWifiInfo.txRetriesRate));
2659                sb.append(String.format(" %.1f ", mWifiInfo.txBadRate));
2660                sb.append(String.format(" rx=%.1f", mWifiInfo.rxSuccessRate));
2661                if (lastScanFreqs != null) {
2662                    sb.append(" list=").append(lastScanFreqs);
2663                } else {
2664                    sb.append(" fiv=").append(fullBandConnectedTimeIntervalMilli);
2665                }
2666                report = reportOnTime();
2667                if (report != null) {
2668                    sb.append(" ").append(report);
2669                }
2670                break;
2671            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
2672                sb.append(" ");
2673                sb.append(Integer.toString(msg.arg1));
2674                sb.append(" ");
2675                sb.append(Integer.toString(msg.arg2));
2676                sb.append(printTime());
2677                StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
2678                if (stateChangeResult != null) {
2679                    sb.append(stateChangeResult.toString());
2680                }
2681                break;
2682            case WifiManager.SAVE_NETWORK:
2683            case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
2684                sb.append(" ");
2685                sb.append(Integer.toString(msg.arg1));
2686                sb.append(" ");
2687                sb.append(Integer.toString(msg.arg2));
2688                if (lastSavedConfigurationAttempt != null) {
2689                    sb.append(" ").append(lastSavedConfigurationAttempt.configKey());
2690                    sb.append(" nid=").append(lastSavedConfigurationAttempt.networkId);
2691                    if (lastSavedConfigurationAttempt.hiddenSSID) {
2692                        sb.append(" hidden");
2693                    }
2694                    if (lastSavedConfigurationAttempt.preSharedKey != null
2695                            && !lastSavedConfigurationAttempt.preSharedKey.equals("*")) {
2696                        sb.append(" hasPSK");
2697                    }
2698                    if (lastSavedConfigurationAttempt.ephemeral) {
2699                        sb.append(" ephemeral");
2700                    }
2701                    if (lastSavedConfigurationAttempt.selfAdded) {
2702                        sb.append(" selfAdded");
2703                    }
2704                    sb.append(" cuid=").append(lastSavedConfigurationAttempt.creatorUid);
2705                    sb.append(" suid=").append(lastSavedConfigurationAttempt.lastUpdateUid);
2706                }
2707                break;
2708            case WifiManager.FORGET_NETWORK:
2709                sb.append(" ");
2710                sb.append(Integer.toString(msg.arg1));
2711                sb.append(" ");
2712                sb.append(Integer.toString(msg.arg2));
2713                if (lastForgetConfigurationAttempt != null) {
2714                    sb.append(" ").append(lastForgetConfigurationAttempt.configKey());
2715                    sb.append(" nid=").append(lastForgetConfigurationAttempt.networkId);
2716                    if (lastForgetConfigurationAttempt.hiddenSSID) {
2717                        sb.append(" hidden");
2718                    }
2719                    if (lastForgetConfigurationAttempt.preSharedKey != null) {
2720                        sb.append(" hasPSK");
2721                    }
2722                    if (lastForgetConfigurationAttempt.ephemeral) {
2723                        sb.append(" ephemeral");
2724                    }
2725                    if (lastForgetConfigurationAttempt.selfAdded) {
2726                        sb.append(" selfAdded");
2727                    }
2728                    sb.append(" cuid=").append(lastForgetConfigurationAttempt.creatorUid);
2729                    sb.append(" suid=").append(lastForgetConfigurationAttempt.lastUpdateUid);
2730                    sb.append(" ajst=").append(lastForgetConfigurationAttempt.autoJoinStatus);
2731                }
2732                break;
2733            case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
2734                sb.append(" ");
2735                sb.append(Integer.toString(msg.arg1));
2736                sb.append(" ");
2737                sb.append(Integer.toString(msg.arg2));
2738                String bssid = (String)msg.obj;
2739                if (bssid != null && bssid.length()>0) {
2740                    sb.append(" ");
2741                    sb.append(bssid);
2742                }
2743                sb.append(" blacklist=" + Boolean.toString(didBlackListBSSID));
2744                sb.append(printTime());
2745                break;
2746            case WifiMonitor.SCAN_RESULTS_EVENT:
2747                sb.append(" ");
2748                sb.append(Integer.toString(msg.arg1));
2749                sb.append(" ");
2750                sb.append(Integer.toString(msg.arg2));
2751                if (mScanResults != null) {
2752                    sb.append(" found=");
2753                    sb.append(mScanResults.size());
2754                }
2755                sb.append(" known=").append(mNumScanResultsKnown);
2756                sb.append(" got=").append(mNumScanResultsReturned);
2757                if (lastScanDuration != 0) {
2758                    sb.append(" dur:").append(lastScanDuration);
2759                }
2760                if (mOnTime != 0) {
2761                    sb.append(" on:").append(mOnTimeThisScan).append(",").append(mOnTimeScan);
2762                    sb.append(",").append(mOnTime);
2763                }
2764                if (mTxTime != 0) {
2765                    sb.append(" tx:").append(mTxTimeThisScan).append(",").append(mTxTimeScan);
2766                    sb.append(",").append(mTxTime);
2767                }
2768                if (mRxTime != 0) {
2769                    sb.append(" rx:").append(mRxTimeThisScan).append(",").append(mRxTimeScan);
2770                    sb.append(",").append(mRxTime);
2771                }
2772                sb.append(String.format(" bcn=%d", mRunningBeaconCount));
2773                sb.append(String.format(" con=%d", mConnectionRequests));
2774                key = mWifiConfigStore.getLastSelectedConfiguration();
2775                if (key != null) {
2776                    sb.append(" last=").append(key);
2777                }
2778                break;
2779            case WifiMonitor.NETWORK_CONNECTION_EVENT:
2780                sb.append(" ");
2781                sb.append(Integer.toString(msg.arg1));
2782                sb.append(" ");
2783                sb.append(Integer.toString(msg.arg2));
2784                sb.append(" ").append(mLastBssid);
2785                sb.append(" nid=").append(mLastNetworkId);
2786                config = getCurrentWifiConfiguration();
2787                if (config != null) {
2788                    sb.append(" ").append(config.configKey());
2789                }
2790                sb.append(printTime());
2791                key = mWifiConfigStore.getLastSelectedConfiguration();
2792                if (key != null) {
2793                    sb.append(" last=").append(key);
2794                }
2795                break;
2796            case CMD_TARGET_BSSID:
2797            case CMD_ASSOCIATED_BSSID:
2798                sb.append(" ");
2799                sb.append(Integer.toString(msg.arg1));
2800                sb.append(" ");
2801                sb.append(Integer.toString(msg.arg2));
2802                if (msg.obj != null) {
2803                    sb.append(" BSSID=").append((String)msg.obj);
2804                }
2805                if (mTargetRoamBSSID != null) {
2806                    sb.append(" Target=").append(mTargetRoamBSSID);
2807                }
2808                sb.append(" roam=").append(Integer.toString(mAutoRoaming));
2809                sb.append(printTime());
2810                break;
2811            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
2812                if (msg.obj != null) {
2813                    sb.append(" ").append((String)msg.obj);
2814                }
2815                sb.append(" nid=").append(msg.arg1);
2816                sb.append(" reason=").append(msg.arg2);
2817                if (mLastBssid != null) {
2818                    sb.append(" lastbssid=").append(mLastBssid);
2819                }
2820                if (mWifiInfo.getFrequency() != -1) {
2821                    sb.append(" freq=").append(mWifiInfo.getFrequency());
2822                    sb.append(" rssi=").append(mWifiInfo.getRssi());
2823                }
2824                if (linkDebouncing) {
2825                    sb.append(" debounce");
2826                }
2827                sb.append(printTime());
2828                break;
2829            case WifiMonitor.SSID_TEMP_DISABLED:
2830            case WifiMonitor.SSID_REENABLED:
2831                sb.append(" nid=").append(msg.arg1);
2832                if (msg.obj != null) {
2833                    sb.append(" ").append((String)msg.obj);
2834                }
2835                config = getCurrentWifiConfiguration();
2836                if (config != null) {
2837                    sb.append(" cur=").append(config.configKey());
2838                    sb.append(" ajst=").append(config.autoJoinStatus);
2839                    if (config.selfAdded) {
2840                        sb.append(" selfAdded");
2841                    }
2842                    if (config.status != 0) {
2843                        sb.append(" st=").append(config.status);
2844                        sb.append(" rs=").append(config.disableReason);
2845                    }
2846                    if (config.lastConnected != 0) {
2847                        now = System.currentTimeMillis();
2848                        sb.append(" lastconn=").append(now - config.lastConnected).append("(ms)");
2849                    }
2850                    if (mLastBssid != null) {
2851                        sb.append(" lastbssid=").append(mLastBssid);
2852                    }
2853                    if (mWifiInfo.getFrequency() != -1) {
2854                        sb.append(" freq=").append(mWifiInfo.getFrequency());
2855                        sb.append(" rssi=").append(mWifiInfo.getRssi());
2856                        sb.append(" bssid=").append(mWifiInfo.getBSSID());
2857                    }
2858                }
2859                sb.append(printTime());
2860                break;
2861            case CMD_RSSI_POLL:
2862            case CMD_UNWANTED_NETWORK:
2863            case WifiManager.RSSI_PKTCNT_FETCH:
2864                sb.append(" ");
2865                sb.append(Integer.toString(msg.arg1));
2866                sb.append(" ");
2867                sb.append(Integer.toString(msg.arg2));
2868                if (mWifiInfo.getSSID() != null)
2869                if (mWifiInfo.getSSID() != null)
2870                    sb.append(" ").append(mWifiInfo.getSSID());
2871                if (mWifiInfo.getBSSID() != null)
2872                    sb.append(" ").append(mWifiInfo.getBSSID());
2873                sb.append(" rssi=").append(mWifiInfo.getRssi());
2874                sb.append(" f=").append(mWifiInfo.getFrequency());
2875                sb.append(" sc=").append(mWifiInfo.score);
2876                sb.append(" link=").append(mWifiInfo.getLinkSpeed());
2877                sb.append(String.format(" tx=%.1f,", mWifiInfo.txSuccessRate));
2878                sb.append(String.format(" %.1f,", mWifiInfo.txRetriesRate));
2879                sb.append(String.format(" %.1f ", mWifiInfo.txBadRate));
2880                sb.append(String.format(" rx=%.1f", mWifiInfo.rxSuccessRate));
2881                sb.append(String.format(" bcn=%d", mRunningBeaconCount));
2882                report = reportOnTime();
2883                if (report != null) {
2884                    sb.append(" ").append(report);
2885                }
2886                if (wifiScoringReport != null) {
2887                    sb.append(wifiScoringReport);
2888                }
2889                break;
2890            case CMD_AUTO_CONNECT:
2891            case WifiManager.CONNECT_NETWORK:
2892                sb.append(" ");
2893                sb.append(Integer.toString(msg.arg1));
2894                sb.append(" ");
2895                sb.append(Integer.toString(msg.arg2));
2896                config = (WifiConfiguration) msg.obj;
2897                if (config != null) {
2898                    sb.append(" ").append(config.configKey());
2899                    if (config.visibility != null) {
2900                        sb.append(" ").append(config.visibility.toString());
2901                    }
2902                }
2903                if (mTargetRoamBSSID != null) {
2904                    sb.append(" ").append(mTargetRoamBSSID);
2905                }
2906                sb.append(" roam=").append(Integer.toString(mAutoRoaming));
2907                sb.append(printTime());
2908                config = getCurrentWifiConfiguration();
2909                if (config != null) {
2910                    sb.append(config.configKey());
2911                    if (config.visibility != null) {
2912                        sb.append(" ").append(config.visibility.toString());
2913                    }
2914                }
2915                break;
2916            case CMD_AUTO_ROAM:
2917                sb.append(" ");
2918                sb.append(Integer.toString(msg.arg1));
2919                sb.append(" ");
2920                sb.append(Integer.toString(msg.arg2));
2921                ScanResult result = (ScanResult)msg.obj;
2922                if (result != null) {
2923                    now = System.currentTimeMillis();
2924                    sb.append(" bssid=").append(result.BSSID);
2925                    sb.append(" rssi=").append(result.level);
2926                    sb.append(" freq=").append(result.frequency);
2927                    if (result.seen > 0 && result.seen < now) {
2928                        sb.append(" seen=").append(now - result.seen);
2929                    } else {
2930                        // Somehow the timestamp for this scan result is inconsistent
2931                        sb.append(" !seen=").append(result.seen);
2932                    }
2933                }
2934                if (mTargetRoamBSSID != null) {
2935                    sb.append(" ").append(mTargetRoamBSSID);
2936                }
2937                sb.append(" roam=").append(Integer.toString(mAutoRoaming));
2938                sb.append(" fail count=").append(Integer.toString(mRoamFailCount));
2939                sb.append(printTime());
2940                break;
2941            case CMD_ADD_OR_UPDATE_NETWORK:
2942                sb.append(" ");
2943                sb.append(Integer.toString(msg.arg1));
2944                sb.append(" ");
2945                sb.append(Integer.toString(msg.arg2));
2946                if (msg.obj != null) {
2947                    config = (WifiConfiguration)msg.obj;
2948                    sb.append(" ").append(config.configKey());
2949                    sb.append(" prio=").append(config.priority);
2950                    sb.append(" status=").append(config.status);
2951                    if (config.BSSID != null) {
2952                        sb.append(" ").append(config.BSSID);
2953                    }
2954                    WifiConfiguration curConfig = getCurrentWifiConfiguration();
2955                    if (curConfig != null) {
2956                        if (curConfig.configKey().equals(config.configKey())) {
2957                            sb.append(" is current");
2958                        } else {
2959                            sb.append(" current=").append(curConfig.configKey());
2960                            sb.append(" prio=").append(curConfig.priority);
2961                            sb.append(" status=").append(curConfig.status);
2962                        }
2963                    }
2964                }
2965                break;
2966            case WifiManager.DISABLE_NETWORK:
2967            case CMD_ENABLE_NETWORK:
2968                sb.append(" ");
2969                sb.append(Integer.toString(msg.arg1));
2970                sb.append(" ");
2971                sb.append(Integer.toString(msg.arg2));
2972                key = mWifiConfigStore.getLastSelectedConfiguration();
2973                if (key != null) {
2974                    sb.append(" last=").append(key);
2975                }
2976                config = mWifiConfigStore.getWifiConfiguration(msg.arg1);
2977                if (config != null && (key == null || !config.configKey().equals(key))) {
2978                    sb.append(" target=").append(key);
2979                }
2980                break;
2981            case CMD_GET_CONFIGURED_NETWORKS:
2982                sb.append(" ");
2983                sb.append(Integer.toString(msg.arg1));
2984                sb.append(" ");
2985                sb.append(Integer.toString(msg.arg2));
2986                sb.append(" num=").append(mWifiConfigStore.getConfiguredNetworksSize());
2987                break;
2988            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
2989                sb.append(" ");
2990                sb.append(Integer.toString(msg.arg1));
2991                sb.append(" ");
2992                sb.append(Integer.toString(msg.arg2));
2993                sb.append(" txpkts=").append(mWifiInfo.txSuccess);
2994                sb.append(",").append(mWifiInfo.txBad);
2995                sb.append(",").append(mWifiInfo.txRetries);
2996                break;
2997            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
2998                sb.append(" ");
2999                sb.append(Integer.toString(msg.arg1));
3000                sb.append(" ");
3001                sb.append(Integer.toString(msg.arg2));
3002                if (msg.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
3003                    sb.append(" OK ");
3004                } else if (msg.arg1 == DhcpStateMachine.DHCP_FAILURE) {
3005                    sb.append(" FAIL ");
3006                }
3007                if (mLinkProperties != null) {
3008                    if (mLinkProperties.hasIPv4Address()) {
3009                        sb.append(" v4");
3010                    }
3011                    if (mLinkProperties.hasGlobalIPv6Address()) {
3012                        sb.append(" v6");
3013                    }
3014                    if (mLinkProperties.hasIPv4DefaultRoute()) {
3015                        sb.append(" v4r");
3016                    }
3017                    if (mLinkProperties.hasIPv6DefaultRoute()) {
3018                        sb.append(" v6r");
3019                    }
3020                    if (mLinkProperties.hasIPv4DnsServer()) {
3021                        sb.append(" v4dns");
3022                    }
3023                    if (mLinkProperties.hasIPv6DnsServer()) {
3024                        sb.append(" v6dns");
3025                    }
3026                }
3027                break;
3028            case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
3029                sb.append(" ");
3030                sb.append(Integer.toString(msg.arg1));
3031                sb.append(" ");
3032                sb.append(Integer.toString(msg.arg2));
3033                if (msg.obj != null) {
3034                    NetworkInfo info = (NetworkInfo)msg.obj;
3035                    NetworkInfo.State state = info.getState();
3036                    NetworkInfo.DetailedState detailedState = info.getDetailedState();
3037                    if (state != null) {
3038                        sb.append(" st=").append(state);
3039                    }
3040                    if (detailedState != null) {
3041                        sb.append("/").append(detailedState);
3042                    }
3043                }
3044                break;
3045            case CMD_IP_CONFIGURATION_LOST:
3046                int count = -1;
3047                WifiConfiguration c = getCurrentWifiConfiguration();
3048                if (c != null) count = c.numIpConfigFailures;
3049                sb.append(" ");
3050                sb.append(Integer.toString(msg.arg1));
3051                sb.append(" ");
3052                sb.append(Integer.toString(msg.arg2));
3053                sb.append(" failures: ");
3054                sb.append(Integer.toString(count));
3055                sb.append("/");
3056                sb.append(Integer.toString(mWifiConfigStore.getMaxDhcpRetries()));
3057                if (mWifiInfo.getBSSID() != null) {
3058                    sb.append(" ").append(mWifiInfo.getBSSID());
3059                }
3060                if (c != null) {
3061                    WifiConfigStore.ScanDetailCache scanDetailCache =
3062                            mWifiConfigStore.getScanDetailCache(c);
3063                    if (scanDetailCache != null) {
3064                        for (ScanDetail sd : scanDetailCache.values()) {
3065                            ScanResult r = sd.getScanResult();
3066                            if (r.BSSID.equals(mWifiInfo.getBSSID())) {
3067                                sb.append(" ipfail=").append(r.numIpConfigFailures);
3068                                sb.append(",st=").append(r.autoJoinStatus);
3069                            }
3070                        }
3071                    }
3072                    sb.append(" -> ajst=").append(c.autoJoinStatus);
3073                    sb.append(" ").append(c.disableReason);
3074                    sb.append(" txpkts=").append(mWifiInfo.txSuccess);
3075                    sb.append(",").append(mWifiInfo.txBad);
3076                    sb.append(",").append(mWifiInfo.txRetries);
3077                }
3078                sb.append(printTime());
3079                sb.append(String.format(" bcn=%d", mRunningBeaconCount));
3080                break;
3081            case CMD_UPDATE_LINKPROPERTIES:
3082                sb.append(" ");
3083                sb.append(Integer.toString(msg.arg1));
3084                sb.append(" ");
3085                sb.append(Integer.toString(msg.arg2));
3086                if (mLinkProperties != null) {
3087                    if (mLinkProperties.hasIPv4Address()) {
3088                        sb.append(" v4");
3089                    }
3090                    if (mLinkProperties.hasGlobalIPv6Address()) {
3091                        sb.append(" v6");
3092                    }
3093                    if (mLinkProperties.hasIPv4DefaultRoute()) {
3094                        sb.append(" v4r");
3095                    }
3096                    if (mLinkProperties.hasIPv6DefaultRoute()) {
3097                        sb.append(" v6r");
3098                    }
3099                    if (mLinkProperties.hasIPv4DnsServer()) {
3100                        sb.append(" v4dns");
3101                    }
3102                    if (mLinkProperties.hasIPv6DnsServer()) {
3103                        sb.append(" v6dns");
3104                    }
3105                }
3106                break;
3107            case CMD_SET_COUNTRY_CODE:
3108                sb.append(" ");
3109                sb.append(Integer.toString(msg.arg1));
3110                sb.append(" ");
3111                sb.append(Integer.toString(msg.arg2));
3112                if (msg.obj != null) {
3113                    sb.append(" ").append((String)msg.obj);
3114                }
3115                break;
3116            case CMD_ROAM_WATCHDOG_TIMER:
3117                sb.append(" ");
3118                sb.append(Integer.toString(msg.arg1));
3119                sb.append(" ");
3120                sb.append(Integer.toString(msg.arg2));
3121                sb.append(" cur=").append(roamWatchdogCount);
3122                break;
3123            case CMD_DISCONNECTING_WATCHDOG_TIMER:
3124                sb.append(" ");
3125                sb.append(Integer.toString(msg.arg1));
3126                sb.append(" ");
3127                sb.append(Integer.toString(msg.arg2));
3128                sb.append(" cur=").append(disconnectingWatchdogCount);
3129                break;
3130            default:
3131                sb.append(" ");
3132                sb.append(Integer.toString(msg.arg1));
3133                sb.append(" ");
3134                sb.append(Integer.toString(msg.arg2));
3135                break;
3136        }
3137
3138        return sb.toString();
3139    }
3140
3141    private void handleScreenStateChanged(boolean screenOn, boolean startBackgroundScanIfNeeded) {
3142        mScreenOn = screenOn;
3143        if (PDBG) {
3144            loge(" handleScreenStateChanged Enter: screenOn=" + screenOn
3145                    + " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt
3146                    + " state " + getCurrentState().getName()
3147                    + " suppState:" + mSupplicantStateTracker.getSupplicantStateName());
3148        }
3149        enableRssiPolling(screenOn);
3150        if (screenOn) enableAllNetworks();
3151        if (mUserWantsSuspendOpt.get()) {
3152            if (screenOn) {
3153                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0);
3154            } else {
3155                // Allow 2s for suspend optimizations to be set
3156                mSuspendWakeLock.acquire(2000);
3157                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0);
3158            }
3159        }
3160        mScreenBroadcastReceived.set(true);
3161
3162        getWifiLinkLayerStats(false);
3163        mOnTimeScreenStateChange = mOnTime;
3164        lastScreenStateChangeTimeStamp = lastLinkLayerStatsUpdate;
3165        mEnableBackgroundScan = false;
3166        cancelDelayedScan();
3167
3168        if (screenOn) {
3169            setScanAlarm(false);
3170            clearBlacklist();
3171
3172            fullBandConnectedTimeIntervalMilli = mWifiConfigStore.associatedPartialScanPeriodMilli.get();
3173            // In either Disconnectedstate or ConnectedState,
3174            // start the scan alarm so as to enable autojoin
3175            if (getCurrentState() == mConnectedState
3176                    && mWifiConfigStore.enableAutoJoinScanWhenAssociated.get()) {
3177                // Scan after 500ms
3178                startDelayedScan(500, null, null);
3179            } else if (getCurrentState() == mDisconnectedState) {
3180                // Scan after 200ms
3181                startDelayedScan(200, null, null);
3182            }
3183        } else if (startBackgroundScanIfNeeded) {
3184            // Screen Off and Disconnected and chipset doesn't support scan offload
3185            //              => start scan alarm
3186            // Screen Off and Disconnected and chipset does support scan offload
3187            //              => will use scan offload (i.e. background scan)
3188            if (!mBackgroundScanSupported) {
3189                setScanAlarm(true);
3190            } else {
3191                mEnableBackgroundScan = true;
3192            }
3193        }
3194        if (DBG) logd("backgroundScan enabled=" + mEnableBackgroundScan
3195                + " startBackgroundScanIfNeeded:" + startBackgroundScanIfNeeded);
3196        if (startBackgroundScanIfNeeded) {
3197            // to scan for them in background, we need all networks enabled
3198            enableBackgroundScan(mEnableBackgroundScan);
3199        }
3200        if (DBG) log("handleScreenStateChanged Exit: " + screenOn);
3201    }
3202
3203    private void checkAndSetConnectivityInstance() {
3204        if (mCm == null) {
3205            mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
3206        }
3207    }
3208
3209    private boolean startTethering(ArrayList<String> available) {
3210
3211        boolean wifiAvailable = false;
3212
3213        checkAndSetConnectivityInstance();
3214
3215        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
3216
3217        for (String intf : available) {
3218            for (String regex : wifiRegexs) {
3219                if (intf.matches(regex)) {
3220
3221                    InterfaceConfiguration ifcg = null;
3222                    try {
3223                        ifcg = mNwService.getInterfaceConfig(intf);
3224                        if (ifcg != null) {
3225                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
3226                            ifcg.setLinkAddress(new LinkAddress(
3227                                    NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
3228                            ifcg.setInterfaceUp();
3229
3230                            mNwService.setInterfaceConfig(intf, ifcg);
3231                        }
3232                    } catch (Exception e) {
3233                        loge("Error configuring interface " + intf + ", :" + e);
3234                        return false;
3235                    }
3236
3237                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
3238                        loge("Error tethering on " + intf);
3239                        return false;
3240                    }
3241                    mTetherInterfaceName = intf;
3242                    return true;
3243                }
3244            }
3245        }
3246        // We found no interfaces to tether
3247        return false;
3248    }
3249
3250    private void stopTethering() {
3251
3252        checkAndSetConnectivityInstance();
3253
3254        /* Clear the interface config to allow dhcp correctly configure new
3255           ip settings */
3256        InterfaceConfiguration ifcg = null;
3257        try {
3258            ifcg = mNwService.getInterfaceConfig(mTetherInterfaceName);
3259            if (ifcg != null) {
3260                ifcg.setLinkAddress(
3261                        new LinkAddress(NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
3262                mNwService.setInterfaceConfig(mTetherInterfaceName, ifcg);
3263            }
3264        } catch (Exception e) {
3265            loge("Error resetting interface " + mTetherInterfaceName + ", :" + e);
3266        }
3267
3268        if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
3269            loge("Untether initiate failed!");
3270        }
3271    }
3272
3273    private boolean isWifiTethered(ArrayList<String> active) {
3274
3275        checkAndSetConnectivityInstance();
3276
3277        String[] wifiRegexs = mCm.getTetherableWifiRegexs();
3278        for (String intf : active) {
3279            for (String regex : wifiRegexs) {
3280                if (intf.matches(regex)) {
3281                    return true;
3282                }
3283            }
3284        }
3285        // We found no interfaces that are tethered
3286        return false;
3287    }
3288
3289    /**
3290     * Set the country code from the system setting value, if any.
3291     */
3292    private void setCountryCode() {
3293        String countryCode = Settings.Global.getString(mContext.getContentResolver(),
3294                Settings.Global.WIFI_COUNTRY_CODE);
3295        if (countryCode != null && !countryCode.isEmpty()) {
3296            setCountryCode(countryCode, false);
3297        } else {
3298            //use driver default
3299        }
3300    }
3301
3302    /**
3303     * Set the frequency band from the system setting value, if any.
3304     */
3305    private void setFrequencyBand() {
3306        int band = Settings.Global.getInt(mContext.getContentResolver(),
3307                Settings.Global.WIFI_FREQUENCY_BAND, WifiManager.WIFI_FREQUENCY_BAND_AUTO);
3308        setFrequencyBand(band, false);
3309    }
3310
3311    private void setSuspendOptimizationsNative(int reason, boolean enabled) {
3312        if (DBG) {
3313            log("setSuspendOptimizationsNative: " + reason + " " + enabled
3314                    + " -want " + mUserWantsSuspendOpt.get()
3315                    + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
3316                    +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
3317                    +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
3318                    +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
3319        }
3320        //mWifiNative.setSuspendOptimizations(enabled);
3321
3322        if (enabled) {
3323            mSuspendOptNeedsDisabled &= ~reason;
3324            /* None of dhcp, screen or highperf need it disabled and user wants it enabled */
3325            if (mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()) {
3326                if (DBG) {
3327                    log("setSuspendOptimizationsNative do it " + reason + " " + enabled
3328                            + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
3329                            +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
3330                            +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
3331                            +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
3332                }
3333                mWifiNative.setSuspendOptimizations(true);
3334            }
3335        } else {
3336            mSuspendOptNeedsDisabled |= reason;
3337            mWifiNative.setSuspendOptimizations(false);
3338        }
3339    }
3340
3341    private void setSuspendOptimizations(int reason, boolean enabled) {
3342        if (DBG) log("setSuspendOptimizations: " + reason + " " + enabled);
3343        if (enabled) {
3344            mSuspendOptNeedsDisabled &= ~reason;
3345        } else {
3346            mSuspendOptNeedsDisabled |= reason;
3347        }
3348        if (DBG) log("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
3349    }
3350
3351    private void setWifiState(int wifiState) {
3352        final int previousWifiState = mWifiState.get();
3353
3354        try {
3355            if (wifiState == WIFI_STATE_ENABLED) {
3356                mBatteryStats.noteWifiOn();
3357            } else if (wifiState == WIFI_STATE_DISABLED) {
3358                mBatteryStats.noteWifiOff();
3359            }
3360        } catch (RemoteException e) {
3361            loge("Failed to note battery stats in wifi");
3362        }
3363
3364        mWifiState.set(wifiState);
3365
3366        if (DBG) log("setWifiState: " + syncGetWifiStateByName());
3367
3368        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
3369        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3370        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
3371        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
3372        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3373    }
3374
3375    private void setWifiApState(int wifiApState) {
3376        final int previousWifiApState = mWifiApState.get();
3377
3378        try {
3379            if (wifiApState == WIFI_AP_STATE_ENABLED) {
3380                mBatteryStats.noteWifiOn();
3381            } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
3382                mBatteryStats.noteWifiOff();
3383            }
3384        } catch (RemoteException e) {
3385            loge("Failed to note battery stats in wifi");
3386        }
3387
3388        // Update state
3389        mWifiApState.set(wifiApState);
3390
3391        if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
3392
3393        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
3394        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
3395        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
3396        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
3397        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
3398    }
3399
3400    /*
3401    void ageOutScanResults(int age) {
3402        synchronized(mScanResultCache) {
3403            // Trim mScanResults, which prevent WifiStateMachine to return
3404            // obsolete scan results to queriers
3405            long now = System.CurrentTimeMillis();
3406            for (int i = 0; i < mScanResults.size(); i++) {
3407                ScanResult result = mScanResults.get(i);
3408                if ((result.seen > now || (now - result.seen) > age)) {
3409                    mScanResults.remove(i);
3410                }
3411            }
3412        }
3413    }*/
3414
3415    private static final String IE_STR = "ie=";
3416    private static final String ID_STR = "id=";
3417    private static final String BSSID_STR = "bssid=";
3418    private static final String FREQ_STR = "freq=";
3419    private static final String LEVEL_STR = "level=";
3420    private static final String TSF_STR = "tsf=";
3421    private static final String FLAGS_STR = "flags=";
3422    private static final String SSID_STR = "ssid=";
3423    private static final String DELIMITER_STR = "====";
3424    private static final String END_STR = "####";
3425
3426    int emptyScanResultCount = 0;
3427
3428    // Used for matching BSSID strings, at least one characteer must be a non-zero number
3429    private static Pattern mNotZero = Pattern.compile("[1-9a-fA-F]");
3430
3431    /**
3432     * Format:
3433     *
3434     * id=1
3435     * bssid=68:7f:76:d7:1a:6e
3436     * freq=2412
3437     * level=-44
3438     * tsf=1344626243700342
3439     * flags=[WPA2-PSK-CCMP][WPS][ESS]
3440     * ssid=zfdy
3441     * ====
3442     * id=2
3443     * bssid=68:5f:74:d7:1a:6f
3444     * freq=5180
3445     * level=-73
3446     * tsf=1344626243700373
3447     * flags=[WPA2-PSK-CCMP][WPS][ESS]
3448     * ssid=zuby
3449     * ====
3450     */
3451    private void setScanResults() {
3452        mNumScanResultsKnown = 0;
3453        mNumScanResultsReturned = 0;
3454        String bssid = "";
3455        int level = 0;
3456        int freq = 0;
3457        long tsf = 0;
3458        String flags = "";
3459        WifiSsid wifiSsid = null;
3460        String scanResults;
3461        String tmpResults;
3462        StringBuffer scanResultsBuf = new StringBuffer();
3463        int sid = 0;
3464
3465        while (true) {
3466            tmpResults = mWifiNative.scanResults(sid);
3467            if (TextUtils.isEmpty(tmpResults)) break;
3468            scanResultsBuf.append(tmpResults);
3469            scanResultsBuf.append("\n");
3470            String[] lines = tmpResults.split("\n");
3471            sid = -1;
3472            for (int i=lines.length - 1; i >= 0; i--) {
3473                if (lines[i].startsWith(END_STR)) {
3474                    break;
3475                } else if (lines[i].startsWith(ID_STR)) {
3476                    try {
3477                        sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
3478                    } catch (NumberFormatException e) {
3479                        // Nothing to do
3480                    }
3481                    break;
3482                }
3483            }
3484            if (sid == -1) break;
3485        }
3486
3487        // Age out scan results, we return all scan results found in the last 12 seconds,
3488        // and NOT all scan results since last scan.
3489        // ageOutScanResults(12000);
3490
3491        scanResults = scanResultsBuf.toString();
3492        if (TextUtils.isEmpty(scanResults)) {
3493            emptyScanResultCount++;
3494            if (emptyScanResultCount > 10) {
3495                // If we got too many empty scan results, the current scan cache is stale,
3496                // hence clear it.
3497                mScanResults = new ArrayList<>();
3498            }
3499           return;
3500        }
3501
3502        emptyScanResultCount = 0;
3503
3504        // note that all these splits and substrings keep references to the original
3505        // huge string buffer while the amount we really want is generally pretty small
3506        // so make copies instead (one example b/11087956 wasted 400k of heap here).
3507        synchronized(mScanResultCache) {
3508            mScanResults = new ArrayList<>();
3509            String[] lines = scanResults.split("\n");
3510            final int bssidStrLen = BSSID_STR.length();
3511            final int flagLen = FLAGS_STR.length();
3512            String infoElements = null;
3513            List<String> anqpLines = null;
3514
3515            for (String line : lines) {
3516                if (line.startsWith(BSSID_STR)) {
3517                    bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
3518                } else if (line.startsWith(FREQ_STR)) {
3519                    try {
3520                        freq = Integer.parseInt(line.substring(FREQ_STR.length()));
3521                    } catch (NumberFormatException e) {
3522                        freq = 0;
3523                    }
3524                } else if (line.startsWith(LEVEL_STR)) {
3525                    try {
3526                        level = Integer.parseInt(line.substring(LEVEL_STR.length()));
3527                        /* some implementations avoid negative values by adding 256
3528                         * so we need to adjust for that here.
3529                         */
3530                        if (level > 0) level -= 256;
3531                    } catch(NumberFormatException e) {
3532                        level = 0;
3533                    }
3534                } else if (line.startsWith(TSF_STR)) {
3535                    try {
3536                        tsf = Long.parseLong(line.substring(TSF_STR.length()));
3537                    } catch (NumberFormatException e) {
3538                        tsf = 0;
3539                    }
3540                } else if (line.startsWith(FLAGS_STR)) {
3541                    flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
3542                } else if (line.startsWith(SSID_STR)) {
3543                    wifiSsid = WifiSsid.createFromAsciiEncoded(
3544                            line.substring(SSID_STR.length()));
3545                } else if (line.startsWith(IE_STR)) {
3546                    infoElements = line;
3547                } else if (SupplicantBridge.isAnqpAttribute(line)) {
3548                    if (anqpLines == null) {
3549                        anqpLines = new ArrayList<>();
3550                    }
3551                    anqpLines.add(line);
3552                } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
3553                    if (bssid != null) {
3554                        try {
3555                            NetworkDetail networkDetail =
3556                                    new NetworkDetail(bssid, infoElements, anqpLines, freq);
3557
3558                            String xssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
3559                            if (!xssid.equals(networkDetail.getSSID())) {
3560                                Log.d("HS2J", "Inconsistency: SSID: '" + xssid +
3561                                        "' vs '" + networkDetail.getSSID() + "': " + infoElements);
3562                            }
3563
3564                            if (networkDetail.hasInterworking()) {
3565                                Log.d("HS2J", "HSNwk: '" + networkDetail);
3566                            }
3567
3568                            ScanDetail scanDetail = mScanResultCache.get(networkDetail);
3569                            if (scanDetail != null) {
3570                                scanDetail.updateResults(networkDetail, level, wifiSsid, xssid,
3571                                        flags, freq, tsf);
3572                            } else {
3573                                scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid,
3574                                        flags, level, freq, tsf);
3575                                mScanResultCache.put(networkDetail, scanDetail);
3576                            }
3577
3578                            mNumScanResultsReturned++; // Keep track of how many scan results we got
3579                            // as part of this scan's processing
3580                            mScanResults.add(scanDetail);
3581                        }
3582                        catch (IllegalArgumentException iae) {
3583                            Log.d("HS2J", "Failed to parse information elements: " + iae);
3584                        } catch (BufferUnderflowException bue) {
3585                            Log.d("HS2J", "Failed to parse information elements: " + bue);
3586                        }
3587                    }
3588                    bssid = null;
3589                    level = 0;
3590                    freq = 0;
3591                    tsf = 0;
3592                    flags = "";
3593                    wifiSsid = null;
3594                    infoElements = null;
3595                    anqpLines = null;
3596                }
3597            }
3598        }
3599
3600        boolean attemptAutoJoin = true;
3601        SupplicantState state = mWifiInfo.getSupplicantState();
3602        String selection = mWifiConfigStore.getLastSelectedConfiguration();
3603        if (getCurrentState() == mRoamingState
3604                || getCurrentState() == mObtainingIpState
3605                || getCurrentState() == mScanModeState
3606                || getCurrentState() == mDisconnectingState
3607                || (getCurrentState() == mConnectedState
3608                && !mWifiConfigStore.enableAutoJoinWhenAssociated.get())
3609                || linkDebouncing
3610                || state == SupplicantState.ASSOCIATING
3611                || state == SupplicantState.AUTHENTICATING
3612                || state == SupplicantState.FOUR_WAY_HANDSHAKE
3613                || state == SupplicantState.GROUP_HANDSHAKE
3614                || (/* keep autojoin enabled if user has manually selected a wifi network,
3615                        so as to make sure we reliably remain connected to this network */
3616                        mConnectionRequests == 0 && selection == null)) {
3617            // Dont attempt auto-joining again while we are already attempting to join
3618            // and/or obtaining Ip address
3619            attemptAutoJoin = false;
3620        }
3621        if (DBG) {
3622            if (selection == null) {
3623                selection = "<none>";
3624            }
3625            loge("wifi setScanResults state" + getCurrentState()
3626                    + " sup_state=" + state
3627                    + " debouncing=" + linkDebouncing
3628                    + " mConnectionRequests=" + mConnectionRequests
3629                    + " selection=" + selection);
3630        }
3631        if (attemptAutoJoin) {
3632            messageHandlingStatus = MESSAGE_HANDLING_STATUS_PROCESSED;
3633        }
3634        // Loose last selected configuration if we have been disconnected for 5 minutes
3635        if (getDisconnectedTimeMilli() > mWifiConfigStore.wifiConfigLastSelectionHysteresis) {
3636            mWifiConfigStore.setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
3637        }
3638
3639        if (mWifiConfigStore.enableAutoJoinWhenAssociated.get()) {
3640            synchronized(mScanResultCache) {
3641                // AutoJoincontroller will directly acces the scan result list and update it with
3642                // ScanResult status
3643                mNumScanResultsKnown = mWifiAutoJoinController.newSupplicantResults(attemptAutoJoin);
3644            }
3645        }
3646        if (linkDebouncing) {
3647            // If debouncing, we dont re-select a SSID or BSSID hence
3648            // there is no need to call the network selection code
3649            // in WifiAutoJoinController, instead,
3650            // just try to reconnect to the same SSID by triggering a roam
3651            sendMessage(CMD_AUTO_ROAM, mLastNetworkId, 1, null);
3652        }
3653    }
3654
3655    /*
3656     * Fetch RSSI, linkspeed, and frequency on current connection
3657     */
3658    private void fetchRssiLinkSpeedAndFrequencyNative() {
3659        int newRssi = -1;
3660        int newLinkSpeed = -1;
3661        int newFrequency = -1;
3662
3663        String signalPoll = mWifiNative.signalPoll();
3664
3665        if (signalPoll != null) {
3666            String[] lines = signalPoll.split("\n");
3667            for (String line : lines) {
3668                String[] prop = line.split("=");
3669                if (prop.length < 2) continue;
3670                try {
3671                    if (prop[0].equals("RSSI")) {
3672                        newRssi = Integer.parseInt(prop[1]);
3673                    } else if (prop[0].equals("LINKSPEED")) {
3674                        newLinkSpeed = Integer.parseInt(prop[1]);
3675                    } else if (prop[0].equals("FREQUENCY")) {
3676                        newFrequency = Integer.parseInt(prop[1]);
3677                    }
3678                } catch (NumberFormatException e) {
3679                    //Ignore, defaults on rssi and linkspeed are assigned
3680                }
3681            }
3682        }
3683
3684        if (PDBG) {
3685            loge("fetchRssiLinkSpeedAndFrequencyNative rssi="
3686                    + Integer.toString(newRssi) + " linkspeed="
3687                    + Integer.toString(newLinkSpeed));
3688        }
3689
3690        if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) {
3691        // screen out invalid values
3692            /* some implementations avoid negative values by adding 256
3693             * so we need to adjust for that here.
3694             */
3695            if (newRssi > 0) newRssi -= 256;
3696            mWifiInfo.setRssi(newRssi);
3697            /*
3698             * Rather then sending the raw RSSI out every time it
3699             * changes, we precalculate the signal level that would
3700             * be displayed in the status bar, and only send the
3701             * broadcast if that much more coarse-grained number
3702             * changes. This cuts down greatly on the number of
3703             * broadcasts, at the cost of not informing others
3704             * interested in RSSI of all the changes in signal
3705             * level.
3706             */
3707            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS);
3708            if (newSignalLevel != mLastSignalLevel) {
3709                sendRssiChangeBroadcast(newRssi);
3710            }
3711            mLastSignalLevel = newSignalLevel;
3712        } else {
3713            mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
3714        }
3715
3716        if (newLinkSpeed != -1) {
3717            mWifiInfo.setLinkSpeed(newLinkSpeed);
3718        }
3719        if (newFrequency > 0) {
3720            if (ScanResult.is5GHz(newFrequency)) {
3721                mWifiConnectionStatistics.num5GhzConnected++;
3722            }
3723            if (ScanResult.is24GHz(newFrequency)) {
3724                mWifiConnectionStatistics.num24GhzConnected++;
3725            }
3726            mWifiInfo.setFrequency(newFrequency);
3727        }
3728        mWifiConfigStore.updateConfiguration(mWifiInfo);
3729    }
3730
3731    /**
3732     *  Determine if we need to switch network:
3733     * - the delta determine the urgency to switch and/or or the expected evilness of the disruption
3734     * - match the uregncy of the switch versus the packet usage at the interface
3735     */
3736    boolean shouldSwitchNetwork(int networkDelta) {
3737        int delta;
3738        if (networkDelta <= 0) {
3739            return false;
3740        }
3741        delta = networkDelta;
3742        if (mWifiInfo != null) {
3743            if (!mWifiConfigStore.enableAutoJoinWhenAssociated.get()
3744                    && mWifiInfo.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
3745                // If AutoJoin while associated is not enabled,
3746                // we should never switch network when already associated
3747                delta = -1000;
3748            } else {
3749                // TODO: Look at per AC packet count, do not switch if VO/VI traffic is present
3750                // TODO: at the interface. We should also discriminate between ucast and mcast,
3751                // TODO: since the rxSuccessRate include all the bonjour and Ipv6
3752                // TODO: broadcasts
3753                if ((mWifiInfo.txSuccessRate > 20) || (mWifiInfo.rxSuccessRate > 80)) {
3754                    delta -= 999;
3755                } else if ((mWifiInfo.txSuccessRate > 5) || (mWifiInfo.rxSuccessRate > 30)) {
3756                    delta -= 6;
3757                }
3758                loge("WifiStateMachine shouldSwitchNetwork "
3759                        + " txSuccessRate=" + String.format("%.2f", mWifiInfo.txSuccessRate)
3760                        + " rxSuccessRate=" + String.format("%.2f", mWifiInfo.rxSuccessRate)
3761                        + " delta " + networkDelta + " -> " + delta);
3762            }
3763        } else {
3764            loge("WifiStateMachine shouldSwitchNetwork "
3765                    + " delta " + networkDelta + " -> " + delta);
3766        }
3767        if (delta > 0) {
3768            return true;
3769        }
3770        return false;
3771    }
3772
3773    // Polling has completed, hence we wont have a score anymore
3774    private void cleanWifiScore() {
3775        mWifiInfo.txBadRate = 0;
3776        mWifiInfo.txSuccessRate = 0;
3777        mWifiInfo.txRetriesRate = 0;
3778        mWifiInfo.rxSuccessRate = 0;
3779    }
3780
3781    int mBadLinkspeedcount = 0;
3782
3783    // For debug, provide information about the last scoring operation
3784    String wifiScoringReport = null;
3785    private void calculateWifiScore(WifiLinkLayerStats stats) {
3786        StringBuilder sb = new StringBuilder();
3787
3788        int score = 56; // Starting score, temporarily hardcoded in between 50 and 60
3789        boolean isBadLinkspeed = (mWifiInfo.is24GHz()
3790                && mWifiInfo.getLinkSpeed() < mWifiConfigStore.badLinkSpeed24)
3791                || (mWifiInfo.is5GHz() && mWifiInfo.getLinkSpeed()
3792                < mWifiConfigStore.badLinkSpeed5);
3793        boolean isGoodLinkspeed = (mWifiInfo.is24GHz()
3794                && mWifiInfo.getLinkSpeed() >= mWifiConfigStore.goodLinkSpeed24)
3795                || (mWifiInfo.is5GHz() && mWifiInfo.getLinkSpeed()
3796                >= mWifiConfigStore.goodLinkSpeed5);
3797
3798        if (isBadLinkspeed) {
3799            if (mBadLinkspeedcount < 6)
3800                mBadLinkspeedcount++;
3801        } else {
3802            if (mBadLinkspeedcount > 0)
3803                mBadLinkspeedcount--;
3804        }
3805
3806        if (isBadLinkspeed) sb.append(" bl(").append(mBadLinkspeedcount).append(")");
3807        if (isGoodLinkspeed) sb.append(" gl");
3808
3809        /**
3810         * We want to make sure that we use the 24GHz RSSI thresholds if
3811         * there are 2.4GHz scan results
3812         * otherwise we end up lowering the score based on 5GHz values
3813         * which may cause a switch to LTE before roaming has a chance to try 2.4GHz
3814         * We also might unblacklist the configuation based on 2.4GHz
3815         * thresholds but joining 5GHz anyhow, and failing over to 2.4GHz because 5GHz is not good
3816         */
3817        boolean use24Thresholds = false;
3818        boolean homeNetworkBoost = false;
3819        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
3820        WifiConfigStore.ScanDetailCache scanDetailCache =
3821                mWifiConfigStore.getScanDetailCache(currentConfiguration);
3822        if (currentConfiguration != null && scanDetailCache != null) {
3823            currentConfiguration.setVisibility(scanDetailCache.getVisibility(12000));
3824            if (currentConfiguration.visibility != null) {
3825                if (currentConfiguration.visibility.rssi24 != WifiConfiguration.INVALID_RSSI
3826                        && currentConfiguration.visibility.rssi24
3827                        >= (currentConfiguration.visibility.rssi5-2)) {
3828                    use24Thresholds = true;
3829                }
3830            }
3831            if (scanDetailCache.size() <= 6
3832                && currentConfiguration.allowedKeyManagement.cardinality() == 1
3833                && currentConfiguration.allowedKeyManagement.
3834                    get(WifiConfiguration.KeyMgmt.WPA_PSK) == true) {
3835                // A PSK network with less than 6 known BSSIDs
3836                // This is most likely a home network and thus we want to stick to wifi more
3837                homeNetworkBoost = true;
3838            }
3839        }
3840        if (homeNetworkBoost) sb.append(" hn");
3841        if (use24Thresholds) sb.append(" u24");
3842
3843        int rssi = mWifiInfo.getRssi() - 6 * mAggressiveHandover
3844                + (homeNetworkBoost ? WifiConfiguration.HOME_NETWORK_RSSI_BOOST : 0);
3845        sb.append(String.format(" rssi=%d ag=%d", rssi, mAggressiveHandover));
3846
3847        boolean is24GHz = use24Thresholds || mWifiInfo.is24GHz();
3848
3849        boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24.get())
3850                || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5.get());
3851        boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24.get())
3852                || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5.get());
3853        boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24.get())
3854                || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5.get());
3855
3856        if (isBadRSSI) sb.append(" br");
3857        if (isLowRSSI) sb.append(" lr");
3858        if (isHighRSSI) sb.append(" hr");
3859
3860        int penalizedDueToUserTriggeredDisconnect = 0;        // For debug information
3861        if (currentConfiguration!= null &&
3862                (mWifiInfo.txSuccessRate > 5 || mWifiInfo.rxSuccessRate > 5)) {
3863            if (isBadRSSI) {
3864                currentConfiguration.numTicksAtBadRSSI++;
3865                if (currentConfiguration.numTicksAtBadRSSI > 1000) {
3866                    // We remained associated for a compound amount of time while passing
3867                    // traffic, hence loose the corresponding user triggered disabled stats
3868                    if (currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0) {
3869                        currentConfiguration.numUserTriggeredWifiDisableBadRSSI--;
3870                    }
3871                    if (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0) {
3872                        currentConfiguration.numUserTriggeredWifiDisableLowRSSI--;
3873                    }
3874                    if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3875                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
3876                    }
3877                    currentConfiguration.numTicksAtBadRSSI = 0;
3878                }
3879                if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
3880                        (currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0
3881                        || currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
3882                        || currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0)) {
3883                    score = score -5;
3884                    penalizedDueToUserTriggeredDisconnect = 1;
3885                    sb.append(" p1");
3886                }
3887            } else if (isLowRSSI) {
3888                currentConfiguration.numTicksAtLowRSSI++;
3889                if (currentConfiguration.numTicksAtLowRSSI > 1000) {
3890                    // We remained associated for a compound amount of time while passing
3891                    // traffic, hence loose the corresponding user triggered disabled stats
3892                    if (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0) {
3893                        currentConfiguration.numUserTriggeredWifiDisableLowRSSI--;
3894                    }
3895                    if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3896                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
3897                    }
3898                    currentConfiguration.numTicksAtLowRSSI = 0;
3899                }
3900                if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
3901                        (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
3902                        || currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0)) {
3903                    score = score -5;
3904                    penalizedDueToUserTriggeredDisconnect = 2;
3905                    sb.append(" p2");
3906                }
3907            } else if (!isHighRSSI) {
3908                currentConfiguration.numTicksAtNotHighRSSI++;
3909                if (currentConfiguration.numTicksAtNotHighRSSI > 1000) {
3910                    // We remained associated for a compound amount of time while passing
3911                    // traffic, hence loose the corresponding user triggered disabled stats
3912                    if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3913                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
3914                    }
3915                    currentConfiguration.numTicksAtNotHighRSSI = 0;
3916                }
3917                if (mWifiConfigStore.enableWifiCellularHandoverUserTriggeredAdjustment &&
3918                        currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
3919                    score = score -5;
3920                    penalizedDueToUserTriggeredDisconnect = 3;
3921                    sb.append(" p3");
3922                }
3923            }
3924            sb.append(String.format(" ticks %d,%d,%d", currentConfiguration.numTicksAtBadRSSI,
3925                    currentConfiguration.numTicksAtLowRSSI,
3926                    currentConfiguration.numTicksAtNotHighRSSI));
3927        }
3928
3929        if (PDBG) {
3930            String rssiStatus = "";
3931            if (isBadRSSI) rssiStatus += " badRSSI ";
3932            else if (isHighRSSI) rssiStatus += " highRSSI ";
3933            else if (isLowRSSI) rssiStatus += " lowRSSI ";
3934            if (isBadLinkspeed) rssiStatus += " lowSpeed ";
3935            loge("calculateWifiScore freq=" + Integer.toString(mWifiInfo.getFrequency())
3936                            + " speed=" + Integer.toString(mWifiInfo.getLinkSpeed())
3937                            + " score=" + Integer.toString(mWifiInfo.score)
3938                            + rssiStatus
3939                            + " -> txbadrate=" + String.format( "%.2f", mWifiInfo.txBadRate )
3940                            + " txgoodrate=" + String.format("%.2f", mWifiInfo.txSuccessRate)
3941                            + " txretriesrate=" + String.format("%.2f", mWifiInfo.txRetriesRate)
3942                            + " rxrate=" + String.format("%.2f", mWifiInfo.rxSuccessRate)
3943                            + " userTriggerdPenalty" + penalizedDueToUserTriggeredDisconnect);
3944        }
3945
3946        if ((mWifiInfo.txBadRate >= 1) && (mWifiInfo.txSuccessRate < 3)
3947                && (isBadRSSI || isLowRSSI)) {
3948            // Link is stuck
3949            if (mWifiInfo.linkStuckCount < 5)
3950                mWifiInfo.linkStuckCount += 1;
3951            sb.append(String.format(" ls+=%d", mWifiInfo.linkStuckCount));
3952            if (PDBG) loge(" bad link -> stuck count ="
3953                    + Integer.toString(mWifiInfo.linkStuckCount));
3954        } else if (mWifiInfo.txSuccessRate > 2 || mWifiInfo.txBadRate < 0.1) {
3955            if (mWifiInfo.linkStuckCount > 0)
3956                mWifiInfo.linkStuckCount -= 1;
3957            sb.append(String.format(" ls-=%d", mWifiInfo.linkStuckCount));
3958            if (PDBG) loge(" good link -> stuck count ="
3959                    + Integer.toString(mWifiInfo.linkStuckCount));
3960        }
3961
3962        sb.append(String.format(" [%d", score));
3963
3964        if (mWifiInfo.linkStuckCount > 1) {
3965            // Once link gets stuck for more than 3 seconds, start reducing the score
3966            score = score - 2 * (mWifiInfo.linkStuckCount - 1);
3967        }
3968        sb.append(String.format(",%d", score));
3969
3970        if (isBadLinkspeed) {
3971            score -= 4 ;
3972            if (PDBG) {
3973                loge(" isBadLinkspeed   ---> count=" + mBadLinkspeedcount
3974                        + " score=" + Integer.toString(score));
3975            }
3976        } else if ((isGoodLinkspeed) && (mWifiInfo.txSuccessRate > 5)) {
3977            score += 4; // So as bad rssi alone dont kill us
3978        }
3979        sb.append(String.format(",%d", score));
3980
3981        if (isBadRSSI) {
3982            if (mWifiInfo.badRssiCount < 7)
3983                mWifiInfo.badRssiCount += 1;
3984        } else if (isLowRSSI) {
3985            mWifiInfo.lowRssiCount = 1; // Dont increment the lowRssi count above 1
3986            if (mWifiInfo.badRssiCount > 0) {
3987                // Decrement bad Rssi count
3988                mWifiInfo.badRssiCount -= 1;
3989            }
3990        } else {
3991            mWifiInfo.badRssiCount = 0;
3992            mWifiInfo.lowRssiCount = 0;
3993        }
3994
3995        score -= mWifiInfo.badRssiCount * 2 +  mWifiInfo.lowRssiCount ;
3996        sb.append(String.format(",%d", score));
3997
3998        if (PDBG) loge(" badRSSI count" + Integer.toString(mWifiInfo.badRssiCount)
3999                     + " lowRSSI count" + Integer.toString(mWifiInfo.lowRssiCount)
4000                        + " --> score " + Integer.toString(score));
4001
4002
4003        if (isHighRSSI) {
4004            score += 5;
4005            if (PDBG) loge(" isHighRSSI       ---> score=" + Integer.toString(score));
4006        }
4007        sb.append(String.format(",%d]", score));
4008
4009        sb.append(String.format(" brc=%d lrc=%d", mWifiInfo.badRssiCount, mWifiInfo.lowRssiCount));
4010
4011        //sanitize boundaries
4012        if (score > NetworkAgent.WIFI_BASE_SCORE)
4013            score = NetworkAgent.WIFI_BASE_SCORE;
4014        if (score < 0)
4015            score = 0;
4016
4017        //report score
4018        if (score != mWifiInfo.score) {
4019            if (DBG) {
4020                loge("calculateWifiScore() report new score " + Integer.toString(score));
4021            }
4022            mWifiInfo.score = score;
4023            if (mNetworkAgent != null) {
4024                mNetworkAgent.sendNetworkScore(score);
4025            }
4026        }
4027        wifiScoringReport = sb.toString();
4028    }
4029
4030    public double getTxPacketRate() {
4031        if (mWifiInfo != null) {
4032            return mWifiInfo.txSuccessRate;
4033        }
4034        return -1;
4035    }
4036
4037    public double getRxPacketRate() {
4038        if (mWifiInfo != null) {
4039            return mWifiInfo.rxSuccessRate;
4040        }
4041        return -1;
4042    }
4043
4044    /**
4045     * Fetch TX packet counters on current connection
4046     */
4047    private void fetchPktcntNative(RssiPacketCountInfo info) {
4048        String pktcntPoll = mWifiNative.pktcntPoll();
4049
4050        if (pktcntPoll != null) {
4051            String[] lines = pktcntPoll.split("\n");
4052            for (String line : lines) {
4053                String[] prop = line.split("=");
4054                if (prop.length < 2) continue;
4055                try {
4056                    if (prop[0].equals("TXGOOD")) {
4057                        info.txgood = Integer.parseInt(prop[1]);
4058                    } else if (prop[0].equals("TXBAD")) {
4059                        info.txbad = Integer.parseInt(prop[1]);
4060                    }
4061                } catch (NumberFormatException e) {
4062                    // Ignore
4063                }
4064            }
4065        }
4066    }
4067
4068    private boolean clearIPv4Address(String iface) {
4069        try {
4070            InterfaceConfiguration ifcg = new InterfaceConfiguration();
4071            ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
4072            mNwService.setInterfaceConfig(iface, ifcg);
4073            return true;
4074        } catch (RemoteException e) {
4075            return false;
4076        }
4077    }
4078
4079    private boolean isProvisioned(LinkProperties lp) {
4080        return lp.isProvisioned() ||
4081               (mWifiConfigStore.isUsingStaticIp(mLastNetworkId) && lp.hasIPv4Address());
4082    }
4083
4084    /**
4085     * Updates mLinkProperties by merging information from various sources.
4086     *
4087     * This is needed because the information in mLinkProperties comes from multiple sources (DHCP,
4088     * netlink, static configuration, ...). When one of these sources of information has updated
4089     * link properties, we can't just assign them to mLinkProperties or we'd lose track of the
4090     * information that came from other sources. Instead, when one of those sources has new
4091     * information, we update the object that tracks the information from that source and then
4092     * call this method to apply the change to mLinkProperties.
4093     *
4094     * The information in mLinkProperties is currently obtained as follows:
4095     * - Interface name: set in the constructor.
4096     * - IPv4 and IPv6 addresses: netlink, passed in by mNetlinkTracker.
4097     * - IPv4 routes, DNS servers, and domains: DHCP.
4098     * - IPv6 routes and DNS servers: netlink, passed in by mNetlinkTracker.
4099     * - HTTP proxy: the wifi config store.
4100     */
4101    private void updateLinkProperties(int reason) {
4102        LinkProperties newLp = new LinkProperties();
4103
4104        // Interface name and proxy are locally configured.
4105        newLp.setInterfaceName(mInterfaceName);
4106        newLp.setHttpProxy(mWifiConfigStore.getProxyProperties(mLastNetworkId));
4107
4108        // IPv4/v6 addresses, IPv6 routes and IPv6 DNS servers come from netlink.
4109        LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
4110        newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
4111        for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
4112            newLp.addRoute(route);
4113        }
4114        for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
4115            newLp.addDnsServer(dns);
4116        }
4117
4118        // IPv4 routes, DNS servers and domains come from mDhcpResults.
4119        synchronized (mDhcpResultsLock) {
4120            // Even when we're using static configuration, we don't need to look at the config
4121            // store, because static IP configuration also populates mDhcpResults.
4122            if ((mDhcpResults != null)) {
4123                for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
4124                    newLp.addRoute(route);
4125                }
4126                for (InetAddress dns : mDhcpResults.dnsServers) {
4127                    newLp.addDnsServer(dns);
4128                }
4129                newLp.setDomains(mDhcpResults.domains);
4130            }
4131        }
4132
4133        final boolean linkChanged = !newLp.equals(mLinkProperties);
4134        final boolean wasProvisioned = isProvisioned(mLinkProperties);
4135        final boolean isProvisioned = isProvisioned(newLp);
4136        final boolean lostIPv4Provisioning =
4137            mLinkProperties.hasIPv4Address() && !newLp.hasIPv4Address();
4138        final DetailedState detailedState = getNetworkDetailedState();
4139
4140        if (linkChanged) {
4141            if (DBG) {
4142                log("Link configuration changed for netId: " + mLastNetworkId
4143                        + " old: " + mLinkProperties + " new: " + newLp);
4144            }
4145            mLinkProperties = newLp;
4146            if (TextUtils.isEmpty(mTcpBufferSizes) == false) {
4147                mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
4148            }
4149            if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
4150        }
4151
4152        if (DBG) {
4153            StringBuilder sb = new StringBuilder();
4154            sb.append("updateLinkProperties nid: " + mLastNetworkId);
4155            sb.append(" state: " + detailedState);
4156            sb.append(" reason: " + smToString(reason));
4157
4158            if (mLinkProperties != null) {
4159                if (mLinkProperties.hasIPv4Address()) {
4160                    sb.append(" v4");
4161                }
4162                if (mLinkProperties.hasGlobalIPv6Address()) {
4163                    sb.append(" v6");
4164                }
4165                if (mLinkProperties.hasIPv4DefaultRoute()) {
4166                    sb.append(" v4r");
4167                }
4168                if (mLinkProperties.hasIPv6DefaultRoute()) {
4169                    sb.append(" v6r");
4170                }
4171                if (mLinkProperties.hasIPv4DnsServer()) {
4172                    sb.append(" v4dns");
4173                }
4174                if (mLinkProperties.hasIPv6DnsServer()) {
4175                    sb.append(" v6dns");
4176                }
4177                if (isProvisioned) {
4178                    sb.append(" isprov");
4179                }
4180            }
4181            loge(sb.toString());
4182        }
4183
4184        // If we just configured or lost IP configuration, do the needful.
4185        // We don't just call handleSuccessfulIpConfiguration() or handleIpConfigurationLost()
4186        // here because those should only be called if we're attempting to connect or already
4187        // connected, whereas updateLinkProperties can be called at any time.
4188        switch (reason) {
4189            case DhcpStateMachine.DHCP_SUCCESS:
4190            case CMD_STATIC_IP_SUCCESS:
4191                // IPv4 provisioning succeded. Advance to connected state.
4192                sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
4193                if (!isProvisioned) {
4194                    // Can never happen unless DHCP reports success but isProvisioned thinks the
4195                    // resulting configuration is invalid (e.g., no IPv4 address, or the state in
4196                    // mLinkProperties is out of sync with reality, or there's a bug in this code).
4197                    // TODO: disconnect here instead. If our configuration is not usable, there's no
4198                    // point in staying connected, and if mLinkProperties is out of sync with
4199                    // reality, that will cause problems in the future.
4200                    loge("IPv4 config succeeded, but not provisioned");
4201                }
4202                break;
4203
4204            case DhcpStateMachine.DHCP_FAILURE:
4205                // DHCP failed. If we're not already provisioned, or we had IPv4 and now lost it,
4206                // give up and disconnect.
4207                // If we're already provisioned (e.g., IPv6-only network), stay connected.
4208                if (!isProvisioned || lostIPv4Provisioning) {
4209                    sendMessage(CMD_IP_CONFIGURATION_LOST);
4210                } else {
4211                    // DHCP failed, but we're provisioned (e.g., if we're on an IPv6-only network).
4212                    sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
4213
4214                    // To be sure we don't get stuck with a non-working network if all we had is
4215                    // IPv4, remove the IPv4 address from the interface (since we're using DHCP,
4216                    // and DHCP failed). If we had an IPv4 address before, the deletion of the
4217                    // address  will cause a CMD_UPDATE_LINKPROPERTIES. If the IPv4 address was
4218                    // necessary for provisioning, its deletion will cause us to disconnect.
4219                    //
4220                    // This shouldn't be needed, because on an IPv4-only network a DHCP failure will
4221                    // have empty DhcpResults and thus empty LinkProperties, and isProvisioned will
4222                    // not return true if we're using DHCP and don't have an IPv4 default route. So
4223                    // for now it's only here for extra redundancy. However, it will increase
4224                    // robustness if we move to getting IPv4 routes from netlink as well.
4225                    loge("DHCP failure: provisioned, clearing IPv4 address.");
4226                    if (!clearIPv4Address(mInterfaceName)) {
4227                        sendMessage(CMD_IP_CONFIGURATION_LOST);
4228                    }
4229                }
4230                break;
4231
4232            case CMD_STATIC_IP_FAILURE:
4233                // Static configuration was invalid, or an error occurred in applying it. Give up.
4234                sendMessage(CMD_IP_CONFIGURATION_LOST);
4235                break;
4236
4237            case CMD_UPDATE_LINKPROPERTIES:
4238                // IP addresses, DNS servers, etc. changed. Act accordingly.
4239                if (wasProvisioned && !isProvisioned) {
4240                    // We no longer have a usable network configuration. Disconnect.
4241                    sendMessage(CMD_IP_CONFIGURATION_LOST);
4242                } else if (!wasProvisioned && isProvisioned) {
4243                    // We have a usable IPv6-only config. Advance to connected state.
4244                    sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
4245                }
4246                if (linkChanged && getNetworkDetailedState() == DetailedState.CONNECTED) {
4247                    // If anything has changed and we're already connected, send out a notification.
4248                    sendLinkConfigurationChangedBroadcast();
4249                }
4250                break;
4251        }
4252    }
4253
4254    /**
4255     * Clears all our link properties.
4256     */
4257     private void clearLinkProperties() {
4258         // Clear the link properties obtained from DHCP and netlink.
4259         synchronized (mDhcpResultsLock) {
4260             if (mDhcpResults != null) {
4261                 mDhcpResults.clear();
4262             }
4263         }
4264         mNetlinkTracker.clearLinkProperties();
4265
4266         // Now clear the merged link properties.
4267         mLinkProperties.clear();
4268         if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);
4269     }
4270
4271     /**
4272      * try to update default route MAC address.
4273      */
4274      private String updateDefaultRouteMacAddress(int timeout) {
4275          String address = null;
4276          for (RouteInfo route : mLinkProperties.getRoutes()) {
4277              if (route.isDefaultRoute() && route.hasGateway()) {
4278                  InetAddress gateway = route.getGateway();
4279                  if (gateway instanceof Inet4Address) {
4280                      if (PDBG) {
4281                          loge("updateDefaultRouteMacAddress found Ipv4 default :"
4282                                  + gateway.getHostAddress());
4283                      }
4284                      address = macAddressFromRoute(gateway.getHostAddress());
4285                     /* The gateway's MAC address is known */
4286                      if ((address == null) && (timeout > 0)) {
4287                          boolean reachable = false;
4288                          try {
4289                              reachable = gateway.isReachable(timeout);
4290                          } catch (Exception e) {
4291                              loge("updateDefaultRouteMacAddress exception reaching :"
4292                                      + gateway.getHostAddress());
4293
4294                          } finally {
4295                              if (reachable == true) {
4296
4297                                  address = macAddressFromRoute(gateway.getHostAddress());
4298                                  if (PDBG) {
4299                                      loge("updateDefaultRouteMacAddress reachable (tried again) :"
4300                                              + gateway.getHostAddress() + " found " + address);
4301                                  }
4302                              }
4303                          }
4304                      }
4305                      if (address != null) {
4306                          mWifiConfigStore.setDefaultGwMacAddress(mLastNetworkId, address);
4307                      }
4308                  }
4309              }
4310          }
4311          return address;
4312      }
4313
4314    private void sendScanResultsAvailableBroadcast() {
4315        Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
4316        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4317        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
4318    }
4319
4320    private void sendRssiChangeBroadcast(final int newRssi) {
4321        try {
4322            mBatteryStats.noteWifiRssiChanged(newRssi);
4323        } catch (RemoteException e) {
4324            // Won't happen.
4325        }
4326        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
4327        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4328        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
4329        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
4330    }
4331
4332    private void sendNetworkStateChangeBroadcast(String bssid) {
4333        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
4334        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4335        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
4336        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
4337        if (bssid != null)
4338            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
4339        if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
4340                mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
4341            intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
4342        }
4343        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
4344    }
4345
4346    private void sendLinkConfigurationChangedBroadcast() {
4347        Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
4348        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4349        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties));
4350        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
4351    }
4352
4353    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
4354        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
4355        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
4356        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
4357        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
4358    }
4359
4360    /**
4361     * Record the detailed state of a network.
4362     * @param state the new {@code DetailedState}
4363     */
4364    private boolean setNetworkDetailedState(NetworkInfo.DetailedState state) {
4365        boolean hidden = false;
4366
4367        if (linkDebouncing || isRoaming()) {
4368            // There is generally a confusion in the system about colluding
4369            // WiFi Layer 2 state (as reported by supplicant) and the Network state
4370            // which leads to multiple confusion.
4371            //
4372            // If link is de-bouncing or roaming, we already have an IP address
4373            // as well we were connected and are doing L2 cycles of
4374            // reconnecting or renewing IP address to check that we still have it
4375            // This L2 link flapping should ne be reflected into the Network state
4376            // which is the state of the WiFi Network visible to Layer 3 and applications
4377            // Note that once debouncing and roaming are completed, we will
4378            // set the Network state to where it should be, or leave it as unchanged
4379            //
4380            hidden = true;
4381        }
4382        if (DBG) {
4383            log("setDetailed state, old ="
4384                    + mNetworkInfo.getDetailedState() + " and new state=" + state
4385                    + " hidden=" + hidden);
4386        }
4387        if (mNetworkInfo.getExtraInfo() != null && mWifiInfo.getSSID() != null) {
4388            // Always indicate that SSID has changed
4389            if (!mNetworkInfo.getExtraInfo().equals(mWifiInfo.getSSID())) {
4390                if (DBG) {
4391                    log("setDetailed state send new extra info"  + mWifiInfo.getSSID());
4392                }
4393                mNetworkInfo.setExtraInfo(mWifiInfo.getSSID());
4394                sendNetworkStateChangeBroadcast(null);
4395            }
4396        }
4397        if (hidden == true) {
4398            return false;
4399        }
4400
4401        if (state != mNetworkInfo.getDetailedState()) {
4402            mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
4403            if (mNetworkAgent != null) {
4404                mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4405            }
4406            sendNetworkStateChangeBroadcast(null);
4407            return true;
4408        }
4409        return false;
4410    }
4411
4412    private DetailedState getNetworkDetailedState() {
4413        return mNetworkInfo.getDetailedState();
4414    }
4415
4416    private SupplicantState handleSupplicantStateChange(Message message) {
4417        StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
4418        SupplicantState state = stateChangeResult.state;
4419        // Supplicant state change
4420        // [31-13] Reserved for future use
4421        // [8 - 0] Supplicant state (as defined in SupplicantState.java)
4422        // 50023 supplicant_state_changed (custom|1|5)
4423        mWifiInfo.setSupplicantState(state);
4424        // Network id is only valid when we start connecting
4425        if (SupplicantState.isConnecting(state)) {
4426            mWifiInfo.setNetworkId(stateChangeResult.networkId);
4427        } else {
4428            mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
4429        }
4430
4431        mWifiInfo.setBSSID(stateChangeResult.BSSID);
4432        mWifiInfo.setSSID(stateChangeResult.wifiSsid);
4433
4434        mSupplicantStateTracker.sendMessage(Message.obtain(message));
4435
4436        return state;
4437    }
4438
4439    /**
4440     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
4441     * using the interface, stopping DHCP & disabling interface
4442     */
4443    private void handleNetworkDisconnect() {
4444        if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP"
4445                + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
4446                +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
4447                +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
4448                +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
4449
4450
4451        clearCurrentConfigBSSID("handleNetworkDisconnect");
4452
4453        stopDhcp();
4454
4455        try {
4456            mNwService.clearInterfaceAddresses(mInterfaceName);
4457            mNwService.disableIpv6(mInterfaceName);
4458        } catch (Exception e) {
4459            loge("Failed to clear addresses or disable ipv6" + e);
4460        }
4461
4462        /* Reset data structures */
4463        mBadLinkspeedcount = 0;
4464        mWifiInfo.reset();
4465        linkDebouncing = false;
4466        /* Reset roaming parameters */
4467        mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
4468
4469        /**
4470         *  fullBandConnectedTimeIntervalMilli:
4471         *  - start scans at mWifiConfigStore.associatedPartialScanPeriodMilli seconds interval
4472         *  - exponentially increase to mWifiConfigStore.associatedFullScanMaxIntervalMilli
4473         *  Initialize to sane value = 20 seconds
4474         */
4475        fullBandConnectedTimeIntervalMilli = 20 * 1000;
4476
4477        setNetworkDetailedState(DetailedState.DISCONNECTED);
4478        if (mNetworkAgent != null) {
4479            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
4480            mNetworkAgent = null;
4481        }
4482        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
4483
4484        /* Clear network properties */
4485        clearLinkProperties();
4486
4487        /* Cend event to CM & network change broadcast */
4488        sendNetworkStateChangeBroadcast(mLastBssid);
4489
4490        /* Cancel auto roam requests */
4491        autoRoamSetBSSID(mLastNetworkId, "any");
4492
4493        mLastBssid= null;
4494        registerDisconnected();
4495        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
4496    }
4497
4498    private void handleSupplicantConnectionLoss() {
4499        /* Socket connection can be lost when we do a graceful shutdown
4500        * or when the driver is hung. Ensure supplicant is stopped here.
4501        */
4502        mWifiMonitor.killSupplicant(mP2pSupported);
4503        mWifiNative.closeSupplicantConnection();
4504        sendSupplicantConnectionChangedBroadcast(false);
4505        setWifiState(WIFI_STATE_DISABLED);
4506    }
4507
4508    void handlePreDhcpSetup() {
4509        mDhcpActive = true;
4510        if (!mBluetoothConnectionActive) {
4511            /*
4512             * There are problems setting the Wi-Fi driver's power
4513             * mode to active when bluetooth coexistence mode is
4514             * enabled or sense.
4515             * <p>
4516             * We set Wi-Fi to active mode when
4517             * obtaining an IP address because we've found
4518             * compatibility issues with some routers with low power
4519             * mode.
4520             * <p>
4521             * In order for this active power mode to properly be set,
4522             * we disable coexistence mode until we're done with
4523             * obtaining an IP address.  One exception is if we
4524             * are currently connected to a headset, since disabling
4525             * coexistence would interrupt that connection.
4526             */
4527            // Disable the coexistence mode
4528            mWifiNative.setBluetoothCoexistenceMode(
4529                    mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
4530        }
4531
4532        // Disable power save and suspend optimizations during DHCP
4533        // Note: The order here is important for now. Brcm driver changes
4534        // power settings when we control suspend mode optimizations.
4535        // TODO: Remove this comment when the driver is fixed.
4536        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
4537        mWifiNative.setPowerSave(false);
4538
4539        // stopBatchedScan();
4540        WifiNative.pauseScan();
4541
4542        // Update link layer stats
4543        getWifiLinkLayerStats(false);
4544
4545        /* P2p discovery breaks dhcp, shut it down in order to get through this */
4546        Message msg = new Message();
4547        msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
4548        msg.arg1 = WifiP2pServiceImpl.ENABLED;
4549        msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
4550        msg.obj = mDhcpStateMachine;
4551        mWifiP2pChannel.sendMessage(msg);
4552    }
4553
4554
4555    private boolean useLegacyDhcpClient() {
4556        return Settings.Global.getInt(
4557                mContext.getContentResolver(),
4558                Settings.Global.LEGACY_DHCP_CLIENT, 0) == 1;
4559    }
4560
4561    private void maybeInitDhcpStateMachine() {
4562        if (mDhcpStateMachine == null) {
4563            if (useLegacyDhcpClient()) {
4564                mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
4565                        mContext, WifiStateMachine.this, mInterfaceName);
4566            } else {
4567                mDhcpStateMachine = DhcpClient.makeDhcpStateMachine(
4568                        mContext, WifiStateMachine.this, mInterfaceName);
4569            }
4570        }
4571    }
4572
4573    void startDhcp() {
4574        maybeInitDhcpStateMachine();
4575        mDhcpStateMachine.registerForPreDhcpNotification();
4576        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
4577    }
4578
4579    void renewDhcp() {
4580        maybeInitDhcpStateMachine();
4581        mDhcpStateMachine.registerForPreDhcpNotification();
4582        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_RENEW_DHCP);
4583    }
4584
4585    void stopDhcp() {
4586        if (mDhcpStateMachine != null) {
4587            /* In case we were in middle of DHCP operation restore back powermode */
4588            handlePostDhcpSetup();
4589            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
4590        }
4591    }
4592
4593    void handlePostDhcpSetup() {
4594        /* Restore power save and suspend optimizations */
4595        setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
4596        mWifiNative.setPowerSave(true);
4597
4598        mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.BLOCK_DISCOVERY, WifiP2pServiceImpl.DISABLED);
4599
4600        // Set the coexistence mode back to its default value
4601        mWifiNative.setBluetoothCoexistenceMode(
4602                mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
4603
4604        mDhcpActive = false;
4605
4606        // startBatchedScan();
4607        WifiNative.restartScan();
4608    }
4609
4610    private void handleIPv4Success(DhcpResults dhcpResults, int reason) {
4611
4612        if (PDBG) {
4613            loge("wifistatemachine handleIPv4Success <" + dhcpResults.toString() + ">");
4614            loge("link address " + dhcpResults.ipAddress);
4615        }
4616
4617        synchronized (mDhcpResultsLock) {
4618            mDhcpResults = dhcpResults;
4619        }
4620
4621        Inet4Address addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
4622        if (isRoaming()) {
4623            if (addr instanceof Inet4Address) {
4624                int previousAddress = mWifiInfo.getIpAddress();
4625                int newAddress = NetworkUtils.inetAddressToInt(addr);
4626                if (previousAddress != newAddress) {
4627                    loge("handleIPv4Success, roaming and address changed" +
4628                            mWifiInfo + " got: " + addr);
4629                } else {
4630
4631                }
4632            } else {
4633                loge("handleIPv4Success, roaming and didnt get an IPv4 address" +
4634                        addr.toString());
4635            }
4636        }
4637        mWifiInfo.setInetAddress(addr);
4638        mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
4639        updateLinkProperties(reason);
4640    }
4641
4642    private void handleSuccessfulIpConfiguration() {
4643        mLastSignalLevel = -1; // Force update of signal strength
4644        WifiConfiguration c = getCurrentWifiConfiguration();
4645        if (c != null) {
4646            // Reset IP failure tracking
4647            c.numConnectionFailures = 0;
4648
4649            // Tell the framework whether the newly connected network is trusted or untrusted.
4650            updateCapabilities(c);
4651        }
4652        if (c != null) {
4653            ScanResult result = getCurrentScanResult();
4654            if (result == null) {
4655                loge("WifiStateMachine: handleSuccessfulIpConfiguration and no scan results" +
4656                        c.configKey());
4657            } else {
4658                // Clear the per BSSID failure count
4659                result.numIpConfigFailures = 0;
4660                // Clear the WHOLE BSSID blacklist, which means supplicant is free to retry
4661                // any BSSID, even though it may already have a non zero ip failure count,
4662                // this will typically happen if the user walks away and come back to his arrea
4663                // TODO: implement blacklisting based on a timer, i.e. keep BSSID blacklisted
4664                // in supplicant for a couple of hours or a day
4665                mWifiNative.clearBlacklist();
4666            }
4667        }
4668    }
4669
4670    private void handleIPv4Failure(int reason) {
4671        synchronized(mDhcpResultsLock) {
4672             if (mDhcpResults != null) {
4673                 mDhcpResults.clear();
4674             }
4675        }
4676        if (PDBG) {
4677            loge("wifistatemachine handleIPv4Failure");
4678        }
4679        updateLinkProperties(reason);
4680    }
4681
4682    private void handleIpConfigurationLost() {
4683        mWifiInfo.setInetAddress(null);
4684        mWifiInfo.setMeteredHint(false);
4685
4686        mWifiConfigStore.handleSSIDStateChange(mLastNetworkId, false,
4687                "DHCP FAILURE", mWifiInfo.getBSSID());
4688
4689        /* DHCP times out after about 30 seconds, we do a
4690         * disconnect thru supplicant, we will let autojoin retry connecting to the network
4691         */
4692        mWifiNative.disconnect();
4693    }
4694
4695    private int convertFrequencyToChannelNumber(int frequency) {
4696        if (frequency >= 2412 && frequency <= 2484) {
4697            return (frequency -2412) / 5 + 1;
4698        } else if (frequency >= 5170  &&  frequency <=5825) {
4699            //DFS is included
4700            return (frequency -5170) / 5 + 34;
4701        } else {
4702            return 0;
4703        }
4704    }
4705
4706    private int chooseApChannel(int apBand) {
4707        int apChannel;
4708        int[] channel;
4709
4710        if (apBand == 0)  {
4711            //for 2.4GHz, we only set the AP at channel 1,6,11
4712            apChannel = 5 * mRandom.nextInt(3) + 1;
4713        } else {
4714            //5G without DFS
4715            channel = mWifiNative.getChannelsForBand(2);
4716            if (channel != null && channel.length > 0) {
4717                apChannel = channel[mRandom.nextInt(channel.length)];
4718                apChannel = convertFrequencyToChannelNumber(apChannel);
4719            } else {
4720                Log.e(TAG, "SoftAp do not get available channel list");
4721                apChannel = 0;
4722            }
4723        }
4724
4725        if(DBG) {
4726            Log.d(TAG, "SoftAp set on channel " + apChannel);
4727        }
4728
4729        return apChannel;
4730    }
4731
4732
4733    /* Current design is to not set the config on a running hostapd but instead
4734     * stop and start tethering when user changes config on a running access point
4735     *
4736     * TODO: Add control channel setup through hostapd that allows changing config
4737     * on a running daemon
4738     */
4739    private void startSoftApWithConfig(final WifiConfiguration configuration) {
4740        // set channel
4741        final WifiConfiguration config = new WifiConfiguration(configuration);
4742
4743        if (DBG) {
4744            Log.d(TAG, "SoftAp config channel is: " + config.apChannel);
4745        }
4746        //set country code through HAL Here
4747        if (mSetCountryCode != null) {
4748            if(!mWifiNative.setCountryCodeHal(mSetCountryCode.toUpperCase(Locale.ROOT))) {
4749                if (config.apBand != 0) {
4750                    Log.e(TAG, "Fail to set country code. Can not setup Softap on 5GHz");
4751                    //countrycode is mandatory for 5GHz
4752                    sendMessage(CMD_START_AP_FAILURE);
4753                    return;
4754                }
4755            }
4756        } else {
4757            if (config.apBand != 0) {
4758                //countrycode is mandatory for 5GHz
4759                Log.e(TAG, "Can not setup softAp on 5GHz without country code!");
4760                sendMessage(CMD_START_AP_FAILURE);
4761                return;
4762            }
4763        }
4764
4765        if (config.apChannel == 0) {
4766            config.apChannel = chooseApChannel(config.apBand);
4767            if (config.apChannel == 0) {
4768                //fail to get available channel
4769                sendMessage(CMD_START_AP_FAILURE);
4770                return;
4771            }
4772        }
4773        //turn off interface
4774        if (!mWifiNative.toggleInterface(0)) {
4775            sendMessage(CMD_START_AP_FAILURE);
4776            return;
4777        }
4778        // Start hostapd on a separate thread
4779        new Thread(new Runnable() {
4780            public void run() {
4781                try {
4782                    mNwService.startAccessPoint(config, mInterfaceName);
4783                } catch (Exception e) {
4784                    loge("Exception in softap start " + e);
4785                    try {
4786                        mNwService.stopAccessPoint(mInterfaceName);
4787                        mNwService.startAccessPoint(config, mInterfaceName);
4788                    } catch (Exception e1) {
4789                        loge("Exception in softap re-start " + e1);
4790                        sendMessage(CMD_START_AP_FAILURE);
4791                        return;
4792                    }
4793                }
4794                if (DBG) log("Soft AP start successful");
4795                sendMessage(CMD_START_AP_SUCCESS);
4796            }
4797        }).start();
4798    }
4799
4800    /*
4801     * Read a MAC address in /proc/arp/table, used by WifistateMachine
4802     * so as to record MAC address of default gateway.
4803     **/
4804    private String macAddressFromRoute(String ipAddress) {
4805        String macAddress = null;
4806        BufferedReader reader = null;
4807        try {
4808            reader = new BufferedReader(new FileReader("/proc/net/arp"));
4809
4810            // Skip over the line bearing colum titles
4811            String line = reader.readLine();
4812
4813            while ((line = reader.readLine()) != null) {
4814                String[] tokens = line.split("[ ]+");
4815                if (tokens.length < 6) {
4816                    continue;
4817                }
4818
4819                // ARP column format is
4820                // Address HWType HWAddress Flags Mask IFace
4821                String ip = tokens[0];
4822                String mac = tokens[3];
4823
4824                if (ipAddress.equals(ip)) {
4825                    macAddress = mac;
4826                    break;
4827                }
4828            }
4829
4830            if (macAddress == null) {
4831                loge("Did not find remoteAddress {" + ipAddress + "} in " +
4832                        "/proc/net/arp");
4833            }
4834
4835        } catch (FileNotFoundException e) {
4836            loge("Could not open /proc/net/arp to lookup mac address");
4837        } catch (IOException e) {
4838            loge("Could not read /proc/net/arp to lookup mac address");
4839        } finally {
4840            try {
4841                if (reader != null) {
4842                    reader.close();
4843                }
4844            } catch (IOException e) {
4845                // Do nothing
4846            }
4847        }
4848        return macAddress;
4849
4850    }
4851
4852    private class WifiNetworkFactory extends NetworkFactory {
4853        public WifiNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities f) {
4854            super(l, c, TAG, f);
4855        }
4856
4857        @Override
4858        protected void needNetworkFor(NetworkRequest networkRequest, int score) {
4859            ++mConnectionRequests;
4860        }
4861
4862        @Override
4863        protected void releaseNetworkFor(NetworkRequest networkRequest) {
4864            --mConnectionRequests;
4865        }
4866
4867        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4868            pw.println("mConnectionRequests " + mConnectionRequests);
4869        }
4870
4871    }
4872
4873    private class UntrustedWifiNetworkFactory extends NetworkFactory {
4874        private int mUntrustedReqCount;
4875
4876        public UntrustedWifiNetworkFactory(Looper l, Context c, String tag, NetworkCapabilities f) {
4877            super(l, c, tag, f);
4878        }
4879
4880        @Override
4881        protected void needNetworkFor(NetworkRequest networkRequest, int score) {
4882            if (!networkRequest.networkCapabilities.hasCapability(
4883                    NetworkCapabilities.NET_CAPABILITY_TRUSTED)) {
4884                if (++mUntrustedReqCount == 1) {
4885                    mWifiAutoJoinController.setAllowUntrustedConnections(true);
4886                }
4887            }
4888        }
4889
4890        @Override
4891        protected void releaseNetworkFor(NetworkRequest networkRequest) {
4892            if (!networkRequest.networkCapabilities.hasCapability(
4893                    NetworkCapabilities.NET_CAPABILITY_TRUSTED)) {
4894                if (--mUntrustedReqCount == 0) {
4895                    mWifiAutoJoinController.setAllowUntrustedConnections(false);
4896                }
4897            }
4898        }
4899
4900        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4901            pw.println("mUntrustedReqCount " + mUntrustedReqCount);
4902        }
4903    }
4904
4905    void maybeRegisterNetworkFactory() {
4906        if (mNetworkFactory == null) {
4907            checkAndSetConnectivityInstance();
4908            if (mCm != null) {
4909                mNetworkFactory = new WifiNetworkFactory(getHandler().getLooper(), mContext,
4910                        NETWORKTYPE, mNetworkCapabilitiesFilter);
4911                mNetworkFactory.setScoreFilter(60);
4912                mNetworkFactory.register();
4913
4914                // We can't filter untrusted network in the capabilities filter because a trusted
4915                // network would still satisfy a request that accepts untrusted ones.
4916                mUntrustedNetworkFactory = new UntrustedWifiNetworkFactory(getHandler().getLooper(),
4917                        mContext, NETWORKTYPE_UNTRUSTED, mNetworkCapabilitiesFilter);
4918                mUntrustedNetworkFactory.setScoreFilter(Integer.MAX_VALUE);
4919                mUntrustedNetworkFactory.register();
4920            }
4921        }
4922    }
4923
4924    /********************************************************
4925     * HSM states
4926     *******************************************************/
4927
4928    class DefaultState extends State {
4929        @Override
4930        public boolean processMessage(Message message) {
4931            logStateAndMessage(message, getClass().getSimpleName());
4932
4933            switch (message.what) {
4934                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
4935                    AsyncChannel ac = (AsyncChannel) message.obj;
4936                    if (ac == mWifiP2pChannel) {
4937                        if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
4938                            mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
4939                        } else {
4940                            loge("WifiP2pService connection failure, error=" + message.arg1);
4941                        }
4942                    } else {
4943                        loge("got HALF_CONNECTED for unknown channel");
4944                    }
4945                    break;
4946                }
4947                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
4948                    AsyncChannel ac = (AsyncChannel) message.obj;
4949                    if (ac == mWifiP2pChannel) {
4950                        loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
4951                        //TODO: Re-establish connection to state machine after a delay
4952                        // mWifiP2pChannel.connect(mContext, getHandler(),
4953                        // mWifiP2pManager.getMessenger());
4954                    }
4955                    break;
4956                }
4957                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
4958                    mBluetoothConnectionActive = (message.arg1 !=
4959                            BluetoothAdapter.STATE_DISCONNECTED);
4960                    break;
4961                    /* Synchronous call returns */
4962                case CMD_PING_SUPPLICANT:
4963                case CMD_ENABLE_NETWORK:
4964                case CMD_ADD_OR_UPDATE_NETWORK:
4965                case CMD_REMOVE_NETWORK:
4966                case CMD_SAVE_CONFIG:
4967                    replyToMessage(message, message.what, FAILURE);
4968                    break;
4969                case CMD_GET_CAPABILITY_FREQ:
4970                    replyToMessage(message, message.what, null);
4971                    break;
4972                case CMD_GET_CONFIGURED_NETWORKS:
4973                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
4974                    break;
4975                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
4976                    replyToMessage(message, message.what, (List<WifiConfiguration>) null);
4977                    break;
4978                case CMD_ENABLE_RSSI_POLL:
4979                    mEnableRssiPolling = (message.arg1 == 1);
4980                    break;
4981                case CMD_SET_HIGH_PERF_MODE:
4982                    if (message.arg1 == 1) {
4983                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, false);
4984                    } else {
4985                        setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
4986                    }
4987                    break;
4988                case CMD_BOOT_COMPLETED:
4989                    maybeRegisterNetworkFactory();
4990                    break;
4991                case CMD_SET_BATCHED_SCAN:
4992                    recordBatchedScanSettings(message.arg1, message.arg2, (Bundle)message.obj);
4993                    break;
4994                case CMD_POLL_BATCHED_SCAN:
4995                    handleBatchedScanPollRequest();
4996                    break;
4997                case CMD_START_NEXT_BATCHED_SCAN:
4998                    startNextBatchedScan();
4999                    break;
5000                case CMD_SCREEN_STATE_CHANGED:
5001                    handleScreenStateChanged(message.arg1 != 0,
5002                            /* startBackgroundScanIfNeeded = */ false);
5003                    break;
5004                    /* Discard */
5005                case CMD_START_SCAN:
5006                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
5007                    break;
5008                case CMD_START_SUPPLICANT:
5009                case CMD_STOP_SUPPLICANT:
5010                case CMD_STOP_SUPPLICANT_FAILED:
5011                case CMD_START_DRIVER:
5012                case CMD_STOP_DRIVER:
5013                case CMD_DELAYED_STOP_DRIVER:
5014                case CMD_DRIVER_START_TIMED_OUT:
5015                case CMD_START_AP:
5016                case CMD_START_AP_SUCCESS:
5017                case CMD_START_AP_FAILURE:
5018                case CMD_STOP_AP:
5019                case CMD_TETHER_STATE_CHANGE:
5020                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
5021                case CMD_DISCONNECT:
5022                case CMD_RECONNECT:
5023                case CMD_REASSOCIATE:
5024                case CMD_RELOAD_TLS_AND_RECONNECT:
5025                case WifiMonitor.SUP_CONNECTION_EVENT:
5026                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5027                case WifiMonitor.NETWORK_CONNECTION_EVENT:
5028                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5029                case WifiMonitor.SCAN_RESULTS_EVENT:
5030                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5031                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5032                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5033                case WifiMonitor.WPS_OVERLAP_EVENT:
5034                case CMD_BLACKLIST_NETWORK:
5035                case CMD_CLEAR_BLACKLIST:
5036                case CMD_SET_OPERATIONAL_MODE:
5037                case CMD_SET_COUNTRY_CODE:
5038                case CMD_SET_FREQUENCY_BAND:
5039                case CMD_RSSI_POLL:
5040                case CMD_ENABLE_ALL_NETWORKS:
5041                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
5042                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
5043                /* Handled by WifiApConfigStore */
5044                case CMD_SET_AP_CONFIG:
5045                case CMD_SET_AP_CONFIG_COMPLETED:
5046                case CMD_REQUEST_AP_CONFIG:
5047                case CMD_RESPONSE_AP_CONFIG:
5048                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
5049                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
5050                case CMD_NO_NETWORKS_PERIODIC_SCAN:
5051                case CMD_DISABLE_P2P_RSP:
5052                case WifiMonitor.SUP_REQUEST_IDENTITY:
5053                case CMD_TEST_NETWORK_DISCONNECT:
5054                case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
5055                case WifiMonitor.SUP_REQUEST_SIM_AUTH:
5056                case CMD_TARGET_BSSID:
5057                case CMD_AUTO_CONNECT:
5058                case CMD_AUTO_ROAM:
5059                case CMD_AUTO_SAVE_NETWORK:
5060                case CMD_ASSOCIATED_BSSID:
5061                case CMD_UNWANTED_NETWORK:
5062                case CMD_DISCONNECTING_WATCHDOG_TIMER:
5063                case CMD_ROAM_WATCHDOG_TIMER:
5064                case CMD_DISABLE_EPHEMERAL_NETWORK:
5065                case CMD_GET_MATCHING_CONFIG:
5066                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
5067                    break;
5068                case DhcpStateMachine.CMD_ON_QUIT:
5069                    mDhcpStateMachine = null;
5070                    break;
5071                case CMD_SET_SUSPEND_OPT_ENABLED:
5072                    if (message.arg1 == 1) {
5073                        mSuspendWakeLock.release();
5074                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, true);
5075                    } else {
5076                        setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
5077                    }
5078                    break;
5079                case WifiMonitor.DRIVER_HUNG_EVENT:
5080                    setSupplicantRunning(false);
5081                    setSupplicantRunning(true);
5082                    break;
5083                case WifiManager.CONNECT_NETWORK:
5084                    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
5085                            WifiManager.BUSY);
5086                    break;
5087                case WifiManager.FORGET_NETWORK:
5088                    replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
5089                            WifiManager.BUSY);
5090                    break;
5091                case WifiManager.SAVE_NETWORK:
5092                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
5093                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
5094                            WifiManager.BUSY);
5095                    break;
5096                case WifiManager.START_WPS:
5097                    replyToMessage(message, WifiManager.WPS_FAILED,
5098                            WifiManager.BUSY);
5099                    break;
5100                case WifiManager.CANCEL_WPS:
5101                    replyToMessage(message, WifiManager.CANCEL_WPS_FAILED,
5102                            WifiManager.BUSY);
5103                    break;
5104                case WifiManager.DISABLE_NETWORK:
5105                    replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
5106                            WifiManager.BUSY);
5107                    break;
5108                case WifiManager.RSSI_PKTCNT_FETCH:
5109                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED,
5110                            WifiManager.BUSY);
5111                    break;
5112                case CMD_GET_SUPPORTED_FEATURES:
5113                    if (WifiNative.startHal()) {
5114                        int featureSet = WifiNative.getSupportedFeatureSet();
5115                        replyToMessage(message, message.what, featureSet);
5116                    } else {
5117                        replyToMessage(message, message.what, 0);
5118                    }
5119                    break;
5120                case CMD_GET_LINK_LAYER_STATS:
5121                    // Not supported hence reply with error message
5122                    replyToMessage(message, message.what, null);
5123                    break;
5124                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
5125                    NetworkInfo info = (NetworkInfo) message.obj;
5126                    mP2pConnected.set(info.isConnected());
5127                    break;
5128                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
5129                    mTemporarilyDisconnectWifi = (message.arg1 == 1);
5130                    replyToMessage(message, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
5131                    break;
5132                /* Link configuration (IP address, DNS, ...) changes notified via netlink */
5133                case CMD_UPDATE_LINKPROPERTIES:
5134                    updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
5135                    break;
5136                case CMD_IP_CONFIGURATION_SUCCESSFUL:
5137                case CMD_IP_CONFIGURATION_LOST:
5138                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
5139                    break;
5140                case CMD_GET_CONNECTION_STATISTICS:
5141                    replyToMessage(message, message.what, mWifiConnectionStatistics);
5142                    break;
5143                case CMD_REMOVE_APP_CONFIGURATIONS:
5144                    deferMessage(message);
5145                    break;
5146                case CMD_REMOVE_USER_CONFIGURATIONS:
5147                    deferMessage(message);
5148                    break;
5149                default:
5150                    loge("Error! unhandled message" + message);
5151                    break;
5152            }
5153            return HANDLED;
5154        }
5155    }
5156
5157    class InitialState extends State {
5158        @Override
5159        public void enter() {
5160            mWifiNative.unloadDriver();
5161
5162            if (mWifiP2pChannel == null) {
5163                mWifiP2pChannel = new AsyncChannel();
5164                mWifiP2pChannel.connect(mContext, getHandler(),
5165                    mWifiP2pServiceImpl.getP2pStateMachineMessenger());
5166            }
5167
5168            if (mWifiApConfigChannel == null) {
5169                mWifiApConfigChannel = new AsyncChannel();
5170                WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
5171                        mContext, getHandler());
5172                wifiApConfigStore.loadApConfiguration();
5173                mWifiApConfigChannel.connectSync(mContext, getHandler(),
5174                        wifiApConfigStore.getMessenger());
5175            }
5176        }
5177        @Override
5178        public boolean processMessage(Message message) {
5179            logStateAndMessage(message, getClass().getSimpleName());
5180            switch (message.what) {
5181                case CMD_START_SUPPLICANT:
5182                    if (mWifiNative.loadDriver()) {
5183                        try {
5184                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
5185                        } catch (Exception e) {
5186                            loge("Failed to reload STA firmware " + e);
5187                            // Continue
5188                        }
5189
5190                        try {
5191                            // A runtime crash can leave the interface up and
5192                            // IP addresses configured, and this affects
5193                            // connectivity when supplicant starts up.
5194                            // Ensure interface is down and we have no IP
5195                            // addresses before a supplicant start.
5196                            mNwService.setInterfaceDown(mInterfaceName);
5197                            mNwService.clearInterfaceAddresses(mInterfaceName);
5198
5199                            // Set privacy extensions
5200                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
5201
5202                           // IPv6 is enabled only as long as access point is connected since:
5203                           // - IPv6 addresses and routes stick around after disconnection
5204                           // - kernel is unaware when connected and fails to start IPv6 negotiation
5205                           // - kernel can start autoconfiguration when 802.1x is not complete
5206                            mNwService.disableIpv6(mInterfaceName);
5207                        } catch (RemoteException re) {
5208                            loge("Unable to change interface settings: " + re);
5209                        } catch (IllegalStateException ie) {
5210                            loge("Unable to change interface settings: " + ie);
5211                        }
5212
5213                       /* Stop a running supplicant after a runtime restart
5214                        * Avoids issues with drivers that do not handle interface down
5215                        * on a running supplicant properly.
5216                        */
5217                        mWifiMonitor.killSupplicant(mP2pSupported);
5218                        if(mWifiNative.startSupplicant(mP2pSupported)) {
5219                            setWifiState(WIFI_STATE_ENABLING);
5220                            if (DBG) log("Supplicant start successful");
5221                            mWifiMonitor.startMonitoring();
5222                            transitionTo(mSupplicantStartingState);
5223                        } else {
5224                            loge("Failed to start supplicant!");
5225                        }
5226                    } else {
5227                        loge("Failed to load driver");
5228                    }
5229                    break;
5230                case CMD_START_AP:
5231                    if (mWifiNative.loadDriver()) {
5232                        setWifiApState(WIFI_AP_STATE_ENABLING);
5233                        transitionTo(mSoftApStartingState);
5234                    } else {
5235                        loge("Failed to load driver for softap");
5236                    }
5237                    break;
5238                default:
5239                    return NOT_HANDLED;
5240            }
5241            return HANDLED;
5242        }
5243    }
5244
5245    class SupplicantStartingState extends State {
5246        private void initializeWpsDetails() {
5247            String detail;
5248            detail = SystemProperties.get("ro.product.name", "");
5249            if (!mWifiNative.setDeviceName(detail)) {
5250                loge("Failed to set device name " +  detail);
5251            }
5252            detail = SystemProperties.get("ro.product.manufacturer", "");
5253            if (!mWifiNative.setManufacturer(detail)) {
5254                loge("Failed to set manufacturer " + detail);
5255            }
5256            detail = SystemProperties.get("ro.product.model", "");
5257            if (!mWifiNative.setModelName(detail)) {
5258                loge("Failed to set model name " + detail);
5259            }
5260            detail = SystemProperties.get("ro.product.model", "");
5261            if (!mWifiNative.setModelNumber(detail)) {
5262                loge("Failed to set model number " + detail);
5263            }
5264            detail = SystemProperties.get("ro.serialno", "");
5265            if (!mWifiNative.setSerialNumber(detail)) {
5266                loge("Failed to set serial number " + detail);
5267            }
5268            if (!mWifiNative.setConfigMethods("physical_display virtual_push_button")) {
5269                loge("Failed to set WPS config methods");
5270            }
5271            if (!mWifiNative.setDeviceType(mPrimaryDeviceType)) {
5272                loge("Failed to set primary device type " + mPrimaryDeviceType);
5273            }
5274        }
5275
5276        @Override
5277        public boolean processMessage(Message message) {
5278            logStateAndMessage(message, getClass().getSimpleName());
5279
5280            switch(message.what) {
5281                case WifiMonitor.SUP_CONNECTION_EVENT:
5282                    if (DBG) log("Supplicant connection established");
5283                    setWifiState(WIFI_STATE_ENABLED);
5284                    mSupplicantRestartCount = 0;
5285                    /* Reset the supplicant state to indicate the supplicant
5286                     * state is not known at this time */
5287                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
5288                    /* Initialize data structures */
5289                    mLastBssid = null;
5290                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
5291                    mLastSignalLevel = -1;
5292
5293                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
5294                    mWifiNative.enableSaveConfig();
5295                    mWifiConfigStore.loadAndEnableAllNetworks();
5296                    if (mWifiConfigStore.enableVerboseLogging.get() > 0) {
5297                        enableVerboseLogging(mWifiConfigStore.enableVerboseLogging.get());
5298                    }
5299                    if (mWifiConfigStore.associatedPartialScanPeriodMilli.get() < 0) {
5300                        mWifiConfigStore.associatedPartialScanPeriodMilli.set(0);
5301                    }
5302                    initializeWpsDetails();
5303
5304                    sendSupplicantConnectionChangedBroadcast(true);
5305                    transitionTo(mDriverStartedState);
5306                    break;
5307                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5308                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
5309                        loge("Failed to setup control channel, restart supplicant");
5310                        mWifiMonitor.killSupplicant(mP2pSupported);
5311                        transitionTo(mInitialState);
5312                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
5313                    } else {
5314                        loge("Failed " + mSupplicantRestartCount +
5315                                " times to start supplicant, unload driver");
5316                        mSupplicantRestartCount = 0;
5317                        setWifiState(WIFI_STATE_UNKNOWN);
5318                        transitionTo(mInitialState);
5319                    }
5320                    break;
5321                case CMD_START_SUPPLICANT:
5322                case CMD_STOP_SUPPLICANT:
5323                case CMD_START_AP:
5324                case CMD_STOP_AP:
5325                case CMD_START_DRIVER:
5326                case CMD_STOP_DRIVER:
5327                case CMD_SET_OPERATIONAL_MODE:
5328                case CMD_SET_COUNTRY_CODE:
5329                case CMD_SET_FREQUENCY_BAND:
5330                case CMD_START_PACKET_FILTERING:
5331                case CMD_STOP_PACKET_FILTERING:
5332                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5333                    deferMessage(message);
5334                    break;
5335                default:
5336                    return NOT_HANDLED;
5337            }
5338            return HANDLED;
5339        }
5340    }
5341
5342    class SupplicantStartedState extends State {
5343        @Override
5344        public void enter() {
5345            /* Wifi is available as long as we have a connection to supplicant */
5346            mNetworkInfo.setIsAvailable(true);
5347            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
5348
5349            int defaultInterval = mContext.getResources().getInteger(
5350                    R.integer.config_wifi_supplicant_scan_interval);
5351
5352            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
5353                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
5354                    defaultInterval);
5355
5356            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
5357            mWifiNative.setExternalSim(true);
5358
5359            /* turn on use of DFS channels */
5360            WifiNative.setDfsFlag(true);
5361
5362            /* set country code */
5363            setCountryCode();
5364
5365            setRandomMacOui();
5366            mWifiNative.enableAutoConnect(false);
5367        }
5368
5369        @Override
5370        public boolean processMessage(Message message) {
5371            logStateAndMessage(message, getClass().getSimpleName());
5372
5373            switch(message.what) {
5374                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
5375                    if (mP2pSupported) {
5376                        transitionTo(mWaitForP2pDisableState);
5377                    } else {
5378                        transitionTo(mSupplicantStoppingState);
5379                    }
5380                    break;
5381                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
5382                    loge("Connection lost, restart supplicant");
5383                    handleSupplicantConnectionLoss();
5384                    handleNetworkDisconnect();
5385                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
5386                    if (mP2pSupported) {
5387                        transitionTo(mWaitForP2pDisableState);
5388                    } else {
5389                        transitionTo(mInitialState);
5390                    }
5391                    sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
5392                    break;
5393                case WifiMonitor.SCAN_RESULTS_EVENT:
5394                    maybeRegisterNetworkFactory(); // Make sure our NetworkFactory is registered
5395                    closeRadioScanStats();
5396                    noteScanEnd();
5397                    setScanResults();
5398                    if (mIsFullScanOngoing || mSendScanResultsBroadcast) {
5399                        /* Just updated results from full scan, let apps know about this */
5400                        sendScanResultsAvailableBroadcast();
5401                    }
5402                    mSendScanResultsBroadcast = false;
5403                    mIsScanOngoing = false;
5404                    mIsFullScanOngoing = false;
5405                    if (mBufferedScanMsg.size() > 0)
5406                        sendMessage(mBufferedScanMsg.remove());
5407                    break;
5408                case CMD_PING_SUPPLICANT:
5409                    boolean ok = mWifiNative.ping();
5410                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
5411                    break;
5412                case CMD_GET_CAPABILITY_FREQ:
5413                    String freqs = mWifiNative.getFreqCapability();
5414                    replyToMessage(message, message.what, freqs);
5415                    break;
5416                case CMD_START_AP:
5417                    /* Cannot start soft AP while in client mode */
5418                    loge("Failed to start soft AP with a running supplicant");
5419                    setWifiApState(WIFI_AP_STATE_FAILED);
5420                    break;
5421                case CMD_SET_OPERATIONAL_MODE:
5422                    mOperationalMode = message.arg1;
5423                    mWifiConfigStore.
5424                            setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
5425                    break;
5426                case CMD_TARGET_BSSID:
5427                    // Trying to associate to this BSSID
5428                    if (message.obj != null) {
5429                        mTargetRoamBSSID = (String) message.obj;
5430                    }
5431                    break;
5432                case CMD_GET_LINK_LAYER_STATS:
5433                    WifiLinkLayerStats stats = getWifiLinkLayerStats(DBG);
5434                    if (stats == null) {
5435                        // When firmware doesnt support link layer stats, return an empty object
5436                        stats = new WifiLinkLayerStats();
5437                    }
5438                    replyToMessage(message, message.what, stats);
5439                    break;
5440                case CMD_SET_COUNTRY_CODE:
5441                    String country = (String) message.obj;
5442
5443                    final boolean persist = (message.arg2 == 1);
5444                    final int sequence = message.arg1;
5445
5446                    if (sequence != mCountryCodeSequence.get()) {
5447                        if (DBG) log("set country code ignored due to sequnce num");
5448                        break;
5449                    }
5450                    if (DBG) log("set country code " + country);
5451                    country = country.toUpperCase(Locale.ROOT);
5452
5453                    if (mDriverSetCountryCode == null || !mDriverSetCountryCode.equals(country)) {
5454                        if (mWifiNative.setCountryCode(country)) {
5455                            mDriverSetCountryCode = country;
5456                        } else {
5457                            loge("Failed to set country code " + country);
5458                        }
5459                    }
5460
5461                    mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.SET_COUNTRY_CODE, country);
5462                    break;
5463                default:
5464                    return NOT_HANDLED;
5465            }
5466            return HANDLED;
5467        }
5468
5469        @Override
5470        public void exit() {
5471            mNetworkInfo.setIsAvailable(false);
5472            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
5473        }
5474    }
5475
5476    class SupplicantStoppingState extends State {
5477        @Override
5478        public void enter() {
5479            /* Send any reset commands to supplicant before shutting it down */
5480            handleNetworkDisconnect();
5481            if (mDhcpStateMachine != null) {
5482                mDhcpStateMachine.doQuit();
5483            }
5484
5485            String suppState = System.getProperty("init.svc.wpa_supplicant");
5486            if (suppState == null) suppState = "unknown";
5487            String p2pSuppState = System.getProperty("init.svc.p2p_supplicant");
5488            if (p2pSuppState == null) p2pSuppState = "unknown";
5489
5490            loge("SupplicantStoppingState: stopSupplicant "
5491                    + " init.svc.wpa_supplicant=" + suppState
5492                    + " init.svc.p2p_supplicant=" + p2pSuppState);
5493            mWifiMonitor.stopSupplicant();
5494
5495            /* Send ourselves a delayed message to indicate failure after a wait time */
5496            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
5497                    ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
5498            setWifiState(WIFI_STATE_DISABLING);
5499            mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
5500        }
5501        @Override
5502        public boolean processMessage(Message message) {
5503            logStateAndMessage(message, getClass().getSimpleName());
5504
5505            switch(message.what) {
5506                case WifiMonitor.SUP_CONNECTION_EVENT:
5507                    loge("Supplicant connection received while stopping");
5508                    break;
5509                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5510                    if (DBG) log("Supplicant connection lost");
5511                    handleSupplicantConnectionLoss();
5512                    transitionTo(mInitialState);
5513                    break;
5514                case CMD_STOP_SUPPLICANT_FAILED:
5515                    if (message.arg1 == mSupplicantStopFailureToken) {
5516                        loge("Timed out on a supplicant stop, kill and proceed");
5517                        handleSupplicantConnectionLoss();
5518                        transitionTo(mInitialState);
5519                    }
5520                    break;
5521                case CMD_START_SUPPLICANT:
5522                case CMD_STOP_SUPPLICANT:
5523                case CMD_START_AP:
5524                case CMD_STOP_AP:
5525                case CMD_START_DRIVER:
5526                case CMD_STOP_DRIVER:
5527                case CMD_SET_OPERATIONAL_MODE:
5528                case CMD_SET_COUNTRY_CODE:
5529                case CMD_SET_FREQUENCY_BAND:
5530                case CMD_START_PACKET_FILTERING:
5531                case CMD_STOP_PACKET_FILTERING:
5532                    deferMessage(message);
5533                    break;
5534                default:
5535                    return NOT_HANDLED;
5536            }
5537            return HANDLED;
5538        }
5539    }
5540
5541    class DriverStartingState extends State {
5542        private int mTries;
5543        @Override
5544        public void enter() {
5545            mTries = 1;
5546            /* Send ourselves a delayed message to start driver a second time */
5547            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
5548                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
5549        }
5550        @Override
5551        public boolean processMessage(Message message) {
5552            logStateAndMessage(message, getClass().getSimpleName());
5553
5554            switch(message.what) {
5555               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5556                    SupplicantState state = handleSupplicantStateChange(message);
5557                    /* If suplicant is exiting out of INTERFACE_DISABLED state into
5558                     * a state that indicates driver has started, it is ready to
5559                     * receive driver commands
5560                     */
5561                    if (SupplicantState.isDriverActive(state)) {
5562                        transitionTo(mDriverStartedState);
5563                    }
5564                    break;
5565                case CMD_DRIVER_START_TIMED_OUT:
5566                    if (message.arg1 == mDriverStartToken) {
5567                        if (mTries >= 2) {
5568                            loge("Failed to start driver after " + mTries);
5569                            transitionTo(mDriverStoppedState);
5570                        } else {
5571                            loge("Driver start failed, retrying");
5572                            mWakeLock.acquire();
5573                            mWifiNative.startDriver();
5574                            mWakeLock.release();
5575
5576                            ++mTries;
5577                            /* Send ourselves a delayed message to start driver again */
5578                            sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
5579                                        ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
5580                        }
5581                    }
5582                    break;
5583                    /* Queue driver commands & connection events */
5584                case CMD_START_DRIVER:
5585                case CMD_STOP_DRIVER:
5586                case WifiMonitor.NETWORK_CONNECTION_EVENT:
5587                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
5588                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
5589                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
5590                case WifiMonitor.WPS_OVERLAP_EVENT:
5591                case CMD_SET_COUNTRY_CODE:
5592                case CMD_SET_FREQUENCY_BAND:
5593                case CMD_START_PACKET_FILTERING:
5594                case CMD_STOP_PACKET_FILTERING:
5595                case CMD_START_SCAN:
5596                case CMD_DISCONNECT:
5597                case CMD_REASSOCIATE:
5598                case CMD_RECONNECT:
5599                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5600                    deferMessage(message);
5601                    break;
5602                case WifiMonitor.SCAN_RESULTS_EVENT:
5603                    // Loose scan results obtained in Driver Starting state, they can only confuse
5604                    // the state machine
5605                    break;
5606                default:
5607                    return NOT_HANDLED;
5608            }
5609            return HANDLED;
5610        }
5611    }
5612
5613    class DriverStartedState extends State {
5614        @Override
5615        public void enter() {
5616
5617            if (PDBG) {
5618                loge("DriverStartedState enter");
5619            }
5620            mIsRunning = true;
5621            mInDelayedStop = false;
5622            mDelayedStopCounter++;
5623            updateBatteryWorkSource(null);
5624            /**
5625             * Enable bluetooth coexistence scan mode when bluetooth connection is active.
5626             * When this mode is on, some of the low-level scan parameters used by the
5627             * driver are changed to reduce interference with bluetooth
5628             */
5629            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
5630            /* set frequency band of operation */
5631            setFrequencyBand();
5632            /* initialize network state */
5633            setNetworkDetailedState(DetailedState.DISCONNECTED);
5634
5635            /* Remove any filtering on Multicast v6 at start */
5636            mWifiNative.stopFilteringMulticastV6Packets();
5637
5638            /* Reset Multicast v4 filtering state */
5639            if (mFilteringMulticastV4Packets.get()) {
5640                mWifiNative.startFilteringMulticastV4Packets();
5641            } else {
5642                mWifiNative.stopFilteringMulticastV4Packets();
5643            }
5644
5645            mDhcpActive = false;
5646
5647            startBatchedScan();
5648
5649            if (mOperationalMode != CONNECT_MODE) {
5650                mWifiNative.disconnect();
5651                mWifiConfigStore.disableAllNetworks();
5652                if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5653                    setWifiState(WIFI_STATE_DISABLED);
5654                }
5655                transitionTo(mScanModeState);
5656            } else {
5657
5658                // Status pulls in the current supplicant state and network connection state
5659                // events over the monitor connection. This helps framework sync up with
5660                // current supplicant state
5661                // TODO: actually check th supplicant status string and make sure the supplicant
5662                // is in disconnecte4d state.
5663                mWifiNative.status();
5664                // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
5665                transitionTo(mDisconnectedState);
5666            }
5667
5668            // We may have missed screen update at boot
5669            if (mScreenBroadcastReceived.get() == false) {
5670                PowerManager powerManager = (PowerManager)mContext.getSystemService(
5671                        Context.POWER_SERVICE);
5672                handleScreenStateChanged(powerManager.isScreenOn(),
5673                        /* startBackgroundScanIfNeeded = */ false);
5674            } else {
5675                // Set the right suspend mode settings
5676                mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
5677                        && mUserWantsSuspendOpt.get());
5678            }
5679            mWifiNative.setPowerSave(true);
5680
5681            if (mP2pSupported) {
5682                if (mOperationalMode == CONNECT_MODE) {
5683                    mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
5684                } else {
5685                    // P2P statemachine starts in disabled state, and is not enabled until
5686                    // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
5687                    // keep it disabled.
5688                }
5689            }
5690
5691            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
5692            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
5693            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
5694            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
5695
5696            if (PDBG) {
5697                loge("Driverstarted State enter done");
5698            }
5699        }
5700
5701        @Override
5702        public boolean processMessage(Message message) {
5703            logStateAndMessage(message, getClass().getSimpleName());
5704
5705            switch(message.what) {
5706                case CMD_START_SCAN:
5707                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
5708                    break;
5709                case CMD_SET_BATCHED_SCAN:
5710                    if (recordBatchedScanSettings(message.arg1, message.arg2,
5711                            (Bundle) message.obj)) {
5712                        if (mBatchedScanSettings != null) {
5713                            startBatchedScan();
5714                        } else {
5715                            stopBatchedScan();
5716                        }
5717                    }
5718                    break;
5719                case CMD_SET_FREQUENCY_BAND:
5720                    int band =  message.arg1;
5721                    if (DBG) log("set frequency band " + band);
5722                    if (mWifiNative.setBand(band)) {
5723
5724                        if (PDBG)  loge("did set frequency band " + band);
5725
5726                        mFrequencyBand.set(band);
5727                        // Flush old data - like scan results
5728                        mWifiNative.bssFlush();
5729                        // Fetch the latest scan results when frequency band is set
5730                        startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, null);
5731
5732                        if (PDBG)  loge("done set frequency band " + band);
5733
5734                    } else {
5735                        loge("Failed to set frequency band " + band);
5736                    }
5737                    break;
5738                case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
5739                    mBluetoothConnectionActive = (message.arg1 !=
5740                            BluetoothAdapter.STATE_DISCONNECTED);
5741                    mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
5742                    break;
5743                case CMD_STOP_DRIVER:
5744                    int mode = message.arg1;
5745
5746                    /* Already doing a delayed stop */
5747                    if (mInDelayedStop) {
5748                        if (DBG) log("Already in delayed stop");
5749                        break;
5750                    }
5751                    /* disconnect right now, but leave the driver running for a bit */
5752                    mWifiConfigStore.disableAllNetworks();
5753
5754                    mInDelayedStop = true;
5755                    mDelayedStopCounter++;
5756                    if (DBG) log("Delayed stop message " + mDelayedStopCounter);
5757
5758                    /* send regular delayed shut down */
5759                    Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
5760                    driverStopIntent.setPackage(this.getClass().getPackage().getName());
5761                    driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
5762                    mDriverStopIntent = PendingIntent.getBroadcast(mContext,
5763                            DRIVER_STOP_REQUEST, driverStopIntent,
5764                            PendingIntent.FLAG_UPDATE_CURRENT);
5765
5766                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
5767                            + mDriverStopDelayMs, mDriverStopIntent);
5768                    break;
5769                case CMD_START_DRIVER:
5770                    if (mInDelayedStop) {
5771                        mInDelayedStop = false;
5772                        mDelayedStopCounter++;
5773                        mAlarmManager.cancel(mDriverStopIntent);
5774                        if (DBG) log("Delayed stop ignored due to start");
5775                        if (mOperationalMode == CONNECT_MODE) {
5776                            mWifiConfigStore.enableAllNetworks();
5777                        }
5778                    }
5779                    break;
5780                case CMD_DELAYED_STOP_DRIVER:
5781                    if (DBG) log("delayed stop " + message.arg1 + " " + mDelayedStopCounter);
5782                    if (message.arg1 != mDelayedStopCounter) break;
5783                    if (getCurrentState() != mDisconnectedState) {
5784                        mWifiNative.disconnect();
5785                        handleNetworkDisconnect();
5786                    }
5787                    mWakeLock.acquire();
5788                    mWifiNative.stopDriver();
5789                    mWakeLock.release();
5790                    if (mP2pSupported) {
5791                        transitionTo(mWaitForP2pDisableState);
5792                    } else {
5793                        transitionTo(mDriverStoppingState);
5794                    }
5795                    break;
5796                case CMD_START_PACKET_FILTERING:
5797                    if (message.arg1 == MULTICAST_V6) {
5798                        mWifiNative.startFilteringMulticastV6Packets();
5799                    } else if (message.arg1 == MULTICAST_V4) {
5800                        mWifiNative.startFilteringMulticastV4Packets();
5801                    } else {
5802                        loge("Illegal arugments to CMD_START_PACKET_FILTERING");
5803                    }
5804                    break;
5805                case CMD_STOP_PACKET_FILTERING:
5806                    if (message.arg1 == MULTICAST_V6) {
5807                        mWifiNative.stopFilteringMulticastV6Packets();
5808                    } else if (message.arg1 == MULTICAST_V4) {
5809                        mWifiNative.stopFilteringMulticastV4Packets();
5810                    } else {
5811                        loge("Illegal arugments to CMD_STOP_PACKET_FILTERING");
5812                    }
5813                    break;
5814                case CMD_SET_SUSPEND_OPT_ENABLED:
5815                    if (message.arg1 == 1) {
5816                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
5817                        mSuspendWakeLock.release();
5818                    } else {
5819                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, false);
5820                    }
5821                    break;
5822                case CMD_SET_HIGH_PERF_MODE:
5823                    if (message.arg1 == 1) {
5824                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, false);
5825                    } else {
5826                        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
5827                    }
5828                    break;
5829                case CMD_ENABLE_TDLS:
5830                    if (message.obj != null) {
5831                        String remoteAddress = (String) message.obj;
5832                        boolean enable = (message.arg1 == 1);
5833                        mWifiNative.startTdls(remoteAddress, enable);
5834                    }
5835                    break;
5836                default:
5837                    return NOT_HANDLED;
5838            }
5839            return HANDLED;
5840        }
5841        @Override
5842        public void exit() {
5843            mIsRunning = false;
5844            updateBatteryWorkSource(null);
5845            mScanResults = new ArrayList<>();
5846
5847            stopBatchedScan();
5848
5849            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
5850            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
5851            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
5852            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
5853            noteScanEnd(); // wrap up any pending request.
5854            mBufferedScanMsg.clear();
5855        }
5856    }
5857
5858    class WaitForP2pDisableState extends State {
5859        private State mTransitionToState;
5860        @Override
5861        public void enter() {
5862            switch (getCurrentMessage().what) {
5863                case WifiMonitor.SUP_DISCONNECTION_EVENT:
5864                    mTransitionToState = mInitialState;
5865                    break;
5866                case CMD_DELAYED_STOP_DRIVER:
5867                    mTransitionToState = mDriverStoppingState;
5868                    break;
5869                case CMD_STOP_SUPPLICANT:
5870                    mTransitionToState = mSupplicantStoppingState;
5871                    break;
5872                default:
5873                    mTransitionToState = mDriverStoppingState;
5874                    break;
5875            }
5876            mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
5877        }
5878        @Override
5879        public boolean processMessage(Message message) {
5880            logStateAndMessage(message, getClass().getSimpleName());
5881
5882            switch(message.what) {
5883                case WifiStateMachine.CMD_DISABLE_P2P_RSP:
5884                    transitionTo(mTransitionToState);
5885                    break;
5886                /* Defer wifi start/shut and driver commands */
5887                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5888                case CMD_START_SUPPLICANT:
5889                case CMD_STOP_SUPPLICANT:
5890                case CMD_START_AP:
5891                case CMD_STOP_AP:
5892                case CMD_START_DRIVER:
5893                case CMD_STOP_DRIVER:
5894                case CMD_SET_OPERATIONAL_MODE:
5895                case CMD_SET_COUNTRY_CODE:
5896                case CMD_SET_FREQUENCY_BAND:
5897                case CMD_START_PACKET_FILTERING:
5898                case CMD_STOP_PACKET_FILTERING:
5899                case CMD_START_SCAN:
5900                case CMD_DISCONNECT:
5901                case CMD_REASSOCIATE:
5902                case CMD_RECONNECT:
5903                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5904                    deferMessage(message);
5905                    break;
5906                default:
5907                    return NOT_HANDLED;
5908            }
5909            return HANDLED;
5910        }
5911    }
5912
5913    class DriverStoppingState extends State {
5914        @Override
5915        public boolean processMessage(Message message) {
5916            logStateAndMessage(message, getClass().getSimpleName());
5917
5918            switch(message.what) {
5919                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5920                    SupplicantState state = handleSupplicantStateChange(message);
5921                    if (state == SupplicantState.INTERFACE_DISABLED) {
5922                        transitionTo(mDriverStoppedState);
5923                    }
5924                    break;
5925                    /* Queue driver commands */
5926                case CMD_START_DRIVER:
5927                case CMD_STOP_DRIVER:
5928                case CMD_SET_COUNTRY_CODE:
5929                case CMD_SET_FREQUENCY_BAND:
5930                case CMD_START_PACKET_FILTERING:
5931                case CMD_STOP_PACKET_FILTERING:
5932                case CMD_START_SCAN:
5933                case CMD_DISCONNECT:
5934                case CMD_REASSOCIATE:
5935                case CMD_RECONNECT:
5936                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
5937                    deferMessage(message);
5938                    break;
5939                default:
5940                    return NOT_HANDLED;
5941            }
5942            return HANDLED;
5943        }
5944    }
5945
5946    class DriverStoppedState extends State {
5947        @Override
5948        public boolean processMessage(Message message) {
5949            logStateAndMessage(message, getClass().getSimpleName());
5950            switch (message.what) {
5951                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
5952                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
5953                    SupplicantState state = stateChangeResult.state;
5954                    // A WEXT bug means that we can be back to driver started state
5955                    // unexpectedly
5956                    if (SupplicantState.isDriverActive(state)) {
5957                        transitionTo(mDriverStartedState);
5958                    }
5959                    break;
5960                case CMD_START_DRIVER:
5961                    mWakeLock.acquire();
5962                    mWifiNative.startDriver();
5963                    mWakeLock.release();
5964                    transitionTo(mDriverStartingState);
5965                    break;
5966                default:
5967                    return NOT_HANDLED;
5968            }
5969            return HANDLED;
5970        }
5971    }
5972
5973    class ScanModeState extends State {
5974        private int mLastOperationMode;
5975        @Override
5976        public void enter() {
5977            mLastOperationMode = mOperationalMode;
5978        }
5979        @Override
5980        public boolean processMessage(Message message) {
5981            logStateAndMessage(message, getClass().getSimpleName());
5982
5983            switch(message.what) {
5984                case CMD_SET_OPERATIONAL_MODE:
5985                    if (message.arg1 == CONNECT_MODE) {
5986
5987                        if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
5988                            setWifiState(WIFI_STATE_ENABLED);
5989                            // Load and re-enable networks when going back to enabled state
5990                            // This is essential for networks to show up after restore
5991                            mWifiConfigStore.loadAndEnableAllNetworks();
5992                            mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
5993                        } else {
5994                            mWifiConfigStore.enableAllNetworks();
5995                        }
5996
5997                        // Try autojoining with recent network already present in the cache
5998                        // If none are found then trigger a scan which will trigger autojoin
5999                        // upon reception of scan results event
6000                        if (!mWifiAutoJoinController.attemptAutoJoin()) {
6001                            startScan(ENABLE_WIFI, 0, null, null);
6002                        }
6003
6004                        // Loose last selection choice since user toggled WiFi
6005                        mWifiConfigStore.
6006                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
6007
6008                        mOperationalMode = CONNECT_MODE;
6009                        transitionTo(mDisconnectedState);
6010                    } else {
6011                        // Nothing to do
6012                        return HANDLED;
6013                    }
6014                    break;
6015                // Handle scan. All the connection related commands are
6016                // handled only in ConnectModeState
6017                case CMD_START_SCAN:
6018                    handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
6019                    break;
6020                default:
6021                    return NOT_HANDLED;
6022            }
6023            return HANDLED;
6024        }
6025    }
6026
6027
6028    String smToString(Message message) {
6029        return smToString(message.what);
6030    }
6031
6032    String smToString(int what) {
6033        String s = "unknown";
6034        switch (what) {
6035            case WifiMonitor.DRIVER_HUNG_EVENT:
6036                s = "DRIVER_HUNG_EVENT";
6037                break;
6038            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
6039                s = "AsyncChannel.CMD_CHANNEL_HALF_CONNECTED";
6040                break;
6041            case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
6042                s = "AsyncChannel.CMD_CHANNEL_DISCONNECTED";
6043                break;
6044            case CMD_SET_FREQUENCY_BAND:
6045                s = "CMD_SET_FREQUENCY_BAND";
6046                break;
6047            case CMD_DELAYED_NETWORK_DISCONNECT:
6048                s = "CMD_DELAYED_NETWORK_DISCONNECT";
6049                break;
6050            case CMD_TEST_NETWORK_DISCONNECT:
6051                s = "CMD_TEST_NETWORK_DISCONNECT";
6052                break;
6053            case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
6054                s = "CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER";
6055                break;
6056            case CMD_DISABLE_EPHEMERAL_NETWORK:
6057                s = "CMD_DISABLE_EPHEMERAL_NETWORK";
6058                break;
6059            case CMD_START_DRIVER:
6060                s = "CMD_START_DRIVER";
6061                break;
6062            case CMD_STOP_DRIVER:
6063                s = "CMD_STOP_DRIVER";
6064                break;
6065            case CMD_STOP_SUPPLICANT:
6066                s = "CMD_STOP_SUPPLICANT";
6067                break;
6068            case CMD_STOP_SUPPLICANT_FAILED:
6069                s = "CMD_STOP_SUPPLICANT_FAILED";
6070                break;
6071            case CMD_START_SUPPLICANT:
6072                s = "CMD_START_SUPPLICANT";
6073                break;
6074            case CMD_REQUEST_AP_CONFIG:
6075                s = "CMD_REQUEST_AP_CONFIG";
6076                break;
6077            case CMD_RESPONSE_AP_CONFIG:
6078                s = "CMD_RESPONSE_AP_CONFIG";
6079                break;
6080            case CMD_TETHER_STATE_CHANGE:
6081                s = "CMD_TETHER_STATE_CHANGE";
6082                break;
6083            case CMD_TETHER_NOTIFICATION_TIMED_OUT:
6084                s = "CMD_TETHER_NOTIFICATION_TIMED_OUT";
6085                break;
6086            case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
6087                s = "CMD_BLUETOOTH_ADAPTER_STATE_CHANGE";
6088                break;
6089            case CMD_ADD_OR_UPDATE_NETWORK:
6090                s = "CMD_ADD_OR_UPDATE_NETWORK";
6091                break;
6092            case CMD_REMOVE_NETWORK:
6093                s = "CMD_REMOVE_NETWORK";
6094                break;
6095            case CMD_ENABLE_NETWORK:
6096                s = "CMD_ENABLE_NETWORK";
6097                break;
6098            case CMD_ENABLE_ALL_NETWORKS:
6099                s = "CMD_ENABLE_ALL_NETWORKS";
6100                break;
6101            case CMD_AUTO_CONNECT:
6102                s = "CMD_AUTO_CONNECT";
6103                break;
6104            case CMD_AUTO_ROAM:
6105                s = "CMD_AUTO_ROAM";
6106                break;
6107            case CMD_AUTO_SAVE_NETWORK:
6108                s = "CMD_AUTO_SAVE_NETWORK";
6109                break;
6110            case CMD_BOOT_COMPLETED:
6111                s = "CMD_BOOT_COMPLETED";
6112                break;
6113            case DhcpStateMachine.CMD_START_DHCP:
6114                s = "CMD_START_DHCP";
6115                break;
6116            case DhcpStateMachine.CMD_STOP_DHCP:
6117                s = "CMD_STOP_DHCP";
6118                break;
6119            case DhcpStateMachine.CMD_RENEW_DHCP:
6120                s = "CMD_RENEW_DHCP";
6121                break;
6122            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
6123                s = "CMD_PRE_DHCP_ACTION";
6124                break;
6125            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
6126                s = "CMD_POST_DHCP_ACTION";
6127                break;
6128            case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
6129                s = "CMD_PRE_DHCP_ACTION_COMPLETE";
6130                break;
6131            case DhcpStateMachine.CMD_ON_QUIT:
6132                s = "CMD_ON_QUIT";
6133                break;
6134            case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
6135                s = "WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST";
6136                break;
6137            case WifiManager.DISABLE_NETWORK:
6138                s = "WifiManager.DISABLE_NETWORK";
6139                break;
6140            case CMD_BLACKLIST_NETWORK:
6141                s = "CMD_BLACKLIST_NETWORK";
6142                break;
6143            case CMD_CLEAR_BLACKLIST:
6144                s = "CMD_CLEAR_BLACKLIST";
6145                break;
6146            case CMD_SAVE_CONFIG:
6147                s = "CMD_SAVE_CONFIG";
6148                break;
6149            case CMD_GET_CONFIGURED_NETWORKS:
6150                s = "CMD_GET_CONFIGURED_NETWORKS";
6151                break;
6152            case CMD_GET_SUPPORTED_FEATURES:
6153                s = "CMD_GET_SUPPORTED_FEATURES";
6154                break;
6155            case CMD_UNWANTED_NETWORK:
6156                s = "CMD_UNWANTED_NETWORK";
6157                break;
6158            case CMD_NETWORK_STATUS:
6159                s = "CMD_NETWORK_STATUS";
6160                break;
6161            case CMD_GET_LINK_LAYER_STATS:
6162                s = "CMD_GET_LINK_LAYER_STATS";
6163                break;
6164            case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
6165                s = "CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS";
6166                break;
6167            case CMD_DISCONNECT:
6168                s = "CMD_DISCONNECT";
6169                break;
6170            case CMD_RECONNECT:
6171                s = "CMD_RECONNECT";
6172                break;
6173            case CMD_REASSOCIATE:
6174                s = "CMD_REASSOCIATE";
6175                break;
6176            case CMD_GET_CONNECTION_STATISTICS:
6177                s = "CMD_GET_CONNECTION_STATISTICS";
6178                break;
6179            case CMD_SET_HIGH_PERF_MODE:
6180                s = "CMD_SET_HIGH_PERF_MODE";
6181                break;
6182            case CMD_SET_COUNTRY_CODE:
6183                s = "CMD_SET_COUNTRY_CODE";
6184                break;
6185            case CMD_ENABLE_RSSI_POLL:
6186                s = "CMD_ENABLE_RSSI_POLL";
6187                break;
6188            case CMD_RSSI_POLL:
6189                s = "CMD_RSSI_POLL";
6190                break;
6191            case CMD_START_PACKET_FILTERING:
6192                s = "CMD_START_PACKET_FILTERING";
6193                break;
6194            case CMD_STOP_PACKET_FILTERING:
6195                s = "CMD_STOP_PACKET_FILTERING";
6196                break;
6197            case CMD_SET_SUSPEND_OPT_ENABLED:
6198                s = "CMD_SET_SUSPEND_OPT_ENABLED";
6199                break;
6200            case CMD_NO_NETWORKS_PERIODIC_SCAN:
6201                s = "CMD_NO_NETWORKS_PERIODIC_SCAN";
6202                break;
6203            case CMD_SET_BATCHED_SCAN:
6204                s = "CMD_SET_BATCHED_SCAN";
6205                break;
6206            case CMD_START_NEXT_BATCHED_SCAN:
6207                s = "CMD_START_NEXT_BATCHED_SCAN";
6208                break;
6209            case CMD_POLL_BATCHED_SCAN:
6210                s = "CMD_POLL_BATCHED_SCAN";
6211                break;
6212            case CMD_UPDATE_LINKPROPERTIES:
6213                s = "CMD_UPDATE_LINKPROPERTIES";
6214                break;
6215            case CMD_RELOAD_TLS_AND_RECONNECT:
6216                s = "CMD_RELOAD_TLS_AND_RECONNECT";
6217                break;
6218            case WifiManager.CONNECT_NETWORK:
6219                s = "CONNECT_NETWORK";
6220                break;
6221            case WifiManager.SAVE_NETWORK:
6222                s = "SAVE_NETWORK";
6223                break;
6224            case WifiManager.FORGET_NETWORK:
6225                s = "FORGET_NETWORK";
6226                break;
6227            case WifiMonitor.SUP_CONNECTION_EVENT:
6228                s = "SUP_CONNECTION_EVENT";
6229                break;
6230            case WifiMonitor.SUP_DISCONNECTION_EVENT:
6231                s = "SUP_DISCONNECTION_EVENT";
6232                break;
6233            case WifiMonitor.SCAN_RESULTS_EVENT:
6234                s = "SCAN_RESULTS_EVENT";
6235                break;
6236            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
6237                s = "SUPPLICANT_STATE_CHANGE_EVENT";
6238                break;
6239            case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
6240                s = "AUTHENTICATION_FAILURE_EVENT";
6241                break;
6242            case WifiMonitor.SSID_TEMP_DISABLED:
6243                s = "SSID_TEMP_DISABLED";
6244                break;
6245            case WifiMonitor.SSID_REENABLED:
6246                s = "SSID_REENABLED";
6247                break;
6248            case WifiMonitor.WPS_SUCCESS_EVENT:
6249                s = "WPS_SUCCESS_EVENT";
6250                break;
6251            case WifiMonitor.WPS_FAIL_EVENT:
6252                s = "WPS_FAIL_EVENT";
6253                break;
6254            case WifiMonitor.SUP_REQUEST_IDENTITY:
6255                s = "SUP_REQUEST_IDENTITY";
6256                break;
6257            case WifiMonitor.NETWORK_CONNECTION_EVENT:
6258                s = "NETWORK_CONNECTION_EVENT";
6259                break;
6260            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
6261                s = "NETWORK_DISCONNECTION_EVENT";
6262                break;
6263            case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
6264                s = "ASSOCIATION_REJECTION_EVENT";
6265                break;
6266            case CMD_SET_OPERATIONAL_MODE:
6267                s = "CMD_SET_OPERATIONAL_MODE";
6268                break;
6269            case CMD_START_SCAN:
6270                s = "CMD_START_SCAN";
6271                break;
6272            case CMD_DISABLE_P2P_RSP:
6273                s = "CMD_DISABLE_P2P_RSP";
6274                break;
6275            case CMD_DISABLE_P2P_REQ:
6276                s = "CMD_DISABLE_P2P_REQ";
6277                break;
6278            case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
6279                s = "GOOD_LINK_DETECTED";
6280                break;
6281            case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
6282                s = "POOR_LINK_DETECTED";
6283                break;
6284            case WifiP2pServiceImpl.GROUP_CREATING_TIMED_OUT:
6285                s = "GROUP_CREATING_TIMED_OUT";
6286                break;
6287            case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
6288                s = "P2P_CONNECTION_CHANGED";
6289                break;
6290            case WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE:
6291                s = "P2P.DISCONNECT_WIFI_RESPONSE";
6292                break;
6293            case WifiP2pServiceImpl.SET_MIRACAST_MODE:
6294                s = "P2P.SET_MIRACAST_MODE";
6295                break;
6296            case WifiP2pServiceImpl.BLOCK_DISCOVERY:
6297                s = "P2P.BLOCK_DISCOVERY";
6298                break;
6299            case WifiP2pServiceImpl.SET_COUNTRY_CODE:
6300                s = "P2P.SET_COUNTRY_CODE";
6301                break;
6302            case WifiManager.CANCEL_WPS:
6303                s = "CANCEL_WPS";
6304                break;
6305            case WifiManager.CANCEL_WPS_FAILED:
6306                s = "CANCEL_WPS_FAILED";
6307                break;
6308            case WifiManager.CANCEL_WPS_SUCCEDED:
6309                s = "CANCEL_WPS_SUCCEDED";
6310                break;
6311            case WifiManager.START_WPS:
6312                s = "START_WPS";
6313                break;
6314            case WifiManager.START_WPS_SUCCEEDED:
6315                s = "START_WPS_SUCCEEDED";
6316                break;
6317            case WifiManager.WPS_FAILED:
6318                s = "WPS_FAILED";
6319                break;
6320            case WifiManager.WPS_COMPLETED:
6321                s = "WPS_COMPLETED";
6322                break;
6323            case WifiManager.RSSI_PKTCNT_FETCH:
6324                s = "RSSI_PKTCNT_FETCH";
6325                break;
6326            case CMD_IP_CONFIGURATION_LOST:
6327                s = "CMD_IP_CONFIGURATION_LOST";
6328                break;
6329            case CMD_IP_CONFIGURATION_SUCCESSFUL:
6330                s = "CMD_IP_CONFIGURATION_SUCCESSFUL";
6331                break;
6332            case CMD_STATIC_IP_SUCCESS:
6333                s = "CMD_STATIC_IP_SUCCESSFUL";
6334                break;
6335            case CMD_STATIC_IP_FAILURE:
6336                s = "CMD_STATIC_IP_FAILURE";
6337                break;
6338            case DhcpStateMachine.DHCP_SUCCESS:
6339                s = "DHCP_SUCCESS";
6340                break;
6341            case DhcpStateMachine.DHCP_FAILURE:
6342                s = "DHCP_FAILURE";
6343                break;
6344            case CMD_TARGET_BSSID:
6345                s = "CMD_TARGET_BSSID";
6346                break;
6347            case CMD_ASSOCIATED_BSSID:
6348                s = "CMD_ASSOCIATED_BSSID";
6349                break;
6350            case CMD_REMOVE_APP_CONFIGURATIONS:
6351                s = "CMD_REMOVE_APP_CONFIGURATIONS";
6352                break;
6353            case CMD_REMOVE_USER_CONFIGURATIONS:
6354                s = "CMD_REMOVE_USER_CONFIGURATIONS";
6355                break;
6356            case CMD_ROAM_WATCHDOG_TIMER:
6357                s = "CMD_ROAM_WATCHDOG_TIMER";
6358                break;
6359            case CMD_SCREEN_STATE_CHANGED:
6360                s = "CMD_SCREEN_STATE_CHANGED";
6361                break;
6362            case CMD_DISCONNECTING_WATCHDOG_TIMER:
6363                s = "CMD_DISCONNECTING_WATCHDOG_TIMER";
6364                break;
6365            default:
6366                s = "what:" + Integer.toString(what);
6367                break;
6368        }
6369        return s;
6370    }
6371
6372    void registerConnected() {
6373       if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
6374           long now_ms = System.currentTimeMillis();
6375           // We are switching away from this configuration,
6376           // hence record the time we were connected last
6377           WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
6378           if (config != null) {
6379               config.lastConnected = System.currentTimeMillis();
6380               config.autoJoinBailedDueToLowRssi = false;
6381               config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
6382               config.numConnectionFailures = 0;
6383               config.numIpConfigFailures = 0;
6384               config.numAuthFailures = 0;
6385               config.numAssociation++;
6386           }
6387           mBadLinkspeedcount = 0;
6388       }
6389    }
6390
6391    void registerDisconnected() {
6392        if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
6393            long now_ms = System.currentTimeMillis();
6394            // We are switching away from this configuration,
6395            // hence record the time we were connected last
6396            WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
6397            if (config != null) {
6398                config.lastDisconnected = System.currentTimeMillis();
6399                if (config.ephemeral) {
6400                    // Remove ephemeral WifiConfigurations from file
6401                    mWifiConfigStore.forgetNetwork(mLastNetworkId);
6402                }
6403            }
6404        }
6405    }
6406
6407    void noteWifiDisabledWhileAssociated() {
6408        // We got disabled by user while we were associated, make note of it
6409        int rssi = mWifiInfo.getRssi();
6410        WifiConfiguration config = getCurrentWifiConfiguration();
6411        if (getCurrentState() == mConnectedState
6412                && rssi != WifiInfo.INVALID_RSSI
6413                && config != null) {
6414            boolean is24GHz = mWifiInfo.is24GHz();
6415            boolean isBadRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdBadRssi24.get())
6416                    || (!is24GHz && rssi < mWifiConfigStore.thresholdBadRssi5.get());
6417            boolean isLowRSSI = (is24GHz && rssi < mWifiConfigStore.thresholdLowRssi24.get())
6418                    || (!is24GHz && mWifiInfo.getRssi() < mWifiConfigStore.thresholdLowRssi5.get());
6419            boolean isHighRSSI = (is24GHz && rssi >= mWifiConfigStore.thresholdGoodRssi24.get())
6420                    || (!is24GHz && mWifiInfo.getRssi() >= mWifiConfigStore.thresholdGoodRssi5.get());
6421            if (isBadRSSI) {
6422                // Take note that we got disabled while RSSI was Bad
6423                config.numUserTriggeredWifiDisableLowRSSI++;
6424            } else if (isLowRSSI) {
6425                // Take note that we got disabled while RSSI was Low
6426                config.numUserTriggeredWifiDisableBadRSSI++;
6427            } else if (!isHighRSSI) {
6428                // Take note that we got disabled while RSSI was Not high
6429                config.numUserTriggeredWifiDisableNotHighRSSI++;
6430            }
6431        }
6432    }
6433
6434    WifiConfiguration getCurrentWifiConfiguration() {
6435        if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
6436            return null;
6437        }
6438        return mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
6439    }
6440
6441    ScanResult getCurrentScanResult() {
6442        WifiConfiguration config = getCurrentWifiConfiguration();
6443        if (config == null) {
6444            return null;
6445        }
6446        String BSSID = mWifiInfo.getBSSID();
6447        if (BSSID == null) {
6448            BSSID = mTargetRoamBSSID;
6449        }
6450        WifiConfigStore.ScanDetailCache scanDetailCache =
6451                mWifiConfigStore.getScanDetailCache(config);
6452
6453        if (scanDetailCache == null) {
6454            return null;
6455        }
6456
6457        return scanDetailCache.get(BSSID);
6458    }
6459
6460    String getCurrentBSSID() {
6461        if (linkDebouncing) {
6462            return null;
6463        }
6464        return mLastBssid;
6465    }
6466
6467    class ConnectModeState extends State {
6468        @Override
6469        public boolean processMessage(Message message) {
6470            WifiConfiguration config;
6471            int netId;
6472            boolean ok;
6473            boolean didDisconnect;
6474            String bssid;
6475            String ssid;
6476            NetworkUpdateResult result;
6477            logStateAndMessage(message, getClass().getSimpleName());
6478
6479            switch (message.what) {
6480                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
6481                    didBlackListBSSID = false;
6482                    bssid = (String) message.obj;
6483                    if (bssid == null || TextUtils.isEmpty(bssid)) {
6484                        // If BSSID is null, use the target roam BSSID
6485                        bssid = mTargetRoamBSSID;
6486                    }
6487                    if (bssid != null) {
6488                        // If we have a BSSID, tell configStore to black list it
6489                        synchronized(mScanResultCache) {
6490                            didBlackListBSSID = mWifiConfigStore.handleBSSIDBlackList
6491                                    (mLastNetworkId, bssid, false);
6492                        }
6493                    }
6494                    mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
6495                    break;
6496                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
6497                    mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
6498                    break;
6499                case WifiMonitor.SSID_TEMP_DISABLED:
6500                case WifiMonitor.SSID_REENABLED:
6501                    String substr = (String) message.obj;
6502                    String en = message.what == WifiMonitor.SSID_TEMP_DISABLED ?
6503                            "temp-disabled" : "re-enabled";
6504                    loge("ConnectModeState SSID state=" + en + " nid="
6505                            + Integer.toString(message.arg1) + " [" + substr + "]");
6506                    synchronized(mScanResultCache) {
6507                        mWifiConfigStore.handleSSIDStateChange(message.arg1, message.what ==
6508                                WifiMonitor.SSID_REENABLED, substr, mWifiInfo.getBSSID());
6509                    }
6510                    break;
6511                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
6512                    SupplicantState state = handleSupplicantStateChange(message);
6513                    // A driver/firmware hang can now put the interface in a down state.
6514                    // We detect the interface going down and recover from it
6515                    if (!SupplicantState.isDriverActive(state)) {
6516                        if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
6517                            handleNetworkDisconnect();
6518                        }
6519                        log("Detected an interface down, restart driver");
6520                        transitionTo(mDriverStoppedState);
6521                        sendMessage(CMD_START_DRIVER);
6522                        break;
6523                    }
6524
6525                    // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT
6526                    // when authentication times out after a successful connection,
6527                    // we can figure this from the supplicant state. If supplicant
6528                    // state is DISCONNECTED, but the mNetworkInfo says we are not
6529                    // disconnected, we need to handle a disconnection
6530                    if (!linkDebouncing && state == SupplicantState.DISCONNECTED &&
6531                            mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) {
6532                        if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect");
6533                        handleNetworkDisconnect();
6534                        transitionTo(mDisconnectedState);
6535                    }
6536                    break;
6537                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
6538                    if (message.arg1 == 1) {
6539                        mWifiNative.disconnect();
6540                        mTemporarilyDisconnectWifi = true;
6541                    } else {
6542                        mWifiNative.reconnect();
6543                        mTemporarilyDisconnectWifi = false;
6544                    }
6545                    break;
6546                case CMD_ADD_OR_UPDATE_NETWORK:
6547                    config = (WifiConfiguration) message.obj;
6548
6549                    if (!recordUidIfAuthorized(config, message.sendingUid)) {
6550                        loge("Not authorized to update network "
6551                             + " config=" + config.SSID
6552                             + " cnid=" + config.networkId
6553                             + " uid=" + message.sendingUid);
6554                        replyToMessage(message, message.what, FAILURE);
6555                        break;
6556                    }
6557
6558                    int res = mWifiConfigStore.addOrUpdateNetwork(config, message.sendingUid);
6559                    if (res < 0) {
6560                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6561                    } else {
6562                        WifiConfiguration curConfig = getCurrentWifiConfiguration();
6563                        if (curConfig != null && config != null) {
6564                            if (curConfig.priority < config.priority
6565                                    && config.status == WifiConfiguration.Status.ENABLED) {
6566                                // Interpret this as a connect attempt
6567                                // Set the last selected configuration so as to allow the system to
6568                                // stick the last user choice without persisting the choice
6569                                mWifiConfigStore.setLastSelectedConfiguration(res);
6570
6571                                // Remember time of last connection attempt
6572                                lastConnectAttempt = System.currentTimeMillis();
6573
6574                                mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6575
6576                                // As a courtesy to the caller, trigger a scan now
6577                                startScan(ADD_OR_UPDATE_SOURCE, 0, null, null);
6578                            }
6579                        }
6580                    }
6581                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK, res);
6582                    break;
6583                case CMD_REMOVE_NETWORK:
6584                    netId = message.arg1;
6585                    if (!mWifiConfigStore.canModifyNetwork(message.sendingUid, netId)) {
6586                        loge("Not authorized to remove network "
6587                             + " cnid=" + netId
6588                             + " uid=" + message.sendingUid);
6589                        replyToMessage(message, message.what, FAILURE);
6590                        break;
6591                    }
6592
6593                    ok = mWifiConfigStore.removeNetwork(message.arg1);
6594                    if (!ok) {
6595                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6596                    }
6597                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
6598                    break;
6599                case CMD_ENABLE_NETWORK:
6600                    boolean others = message.arg2 == 1;
6601                    // Tell autojoin the user did try to select to that network
6602                    // However, do NOT persist the choice by bumping the priority of the network
6603                    if (others) {
6604                        mWifiAutoJoinController.
6605                                updateConfigurationHistory(message.arg1, true, false);
6606                        // Set the last selected configuration so as to allow the system to
6607                        // stick the last user choice without persisting the choice
6608                        mWifiConfigStore.setLastSelectedConfiguration(message.arg1);
6609
6610                        // Remember time of last connection attempt
6611                        lastConnectAttempt = System.currentTimeMillis();
6612
6613                        mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6614                    }
6615                    // Cancel auto roam requests
6616                    autoRoamSetBSSID(message.arg1, "any");
6617
6618                    int uid = message.sendingUid;
6619                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1, uid);
6620                    if (!ok) {
6621                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6622                    }
6623                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
6624                    break;
6625                case CMD_ENABLE_ALL_NETWORKS:
6626                    long time = android.os.SystemClock.elapsedRealtime();
6627                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
6628                        mWifiConfigStore.enableAllNetworks();
6629                        mLastEnableAllNetworksTime = time;
6630                    }
6631                    break;
6632                case WifiManager.DISABLE_NETWORK:
6633                    if (mWifiConfigStore.disableNetwork(message.arg1,
6634                            WifiConfiguration.DISABLED_BY_WIFI_MANAGER) == true) {
6635                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
6636                    } else {
6637                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
6638                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
6639                                WifiManager.ERROR);
6640                    }
6641                    break;
6642                case CMD_DISABLE_EPHEMERAL_NETWORK:
6643                    config = mWifiConfigStore.disableEphemeralNetwork((String)message.obj);
6644                    if (config != null) {
6645                        if (config.networkId == mLastNetworkId) {
6646                            // Disconnect and let autojoin reselect a new network
6647                            sendMessage(CMD_DISCONNECT);
6648                        }
6649                    }
6650                    break;
6651                case CMD_BLACKLIST_NETWORK:
6652                    mWifiNative.addToBlacklist((String) message.obj);
6653                    break;
6654                case CMD_CLEAR_BLACKLIST:
6655                    mWifiNative.clearBlacklist();
6656                    break;
6657                case CMD_SAVE_CONFIG:
6658                    ok = mWifiConfigStore.saveConfig();
6659
6660                    if (DBG) loge("wifistatemachine did save config " + ok);
6661                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
6662
6663                    // Inform the backup manager about a data change
6664                    IBackupManager ibm = IBackupManager.Stub.asInterface(
6665                            ServiceManager.getService(Context.BACKUP_SERVICE));
6666                    if (ibm != null) {
6667                        try {
6668                            ibm.dataChanged("com.android.providers.settings");
6669                        } catch (Exception e) {
6670                            // Try again later
6671                        }
6672                    }
6673                    break;
6674                case CMD_GET_CONFIGURED_NETWORKS:
6675                    replyToMessage(message, message.what,
6676                            mWifiConfigStore.getConfiguredNetworks());
6677                    break;
6678                case WifiMonitor.SUP_REQUEST_IDENTITY:
6679                    int networkId = message.arg2;
6680                    boolean identitySent = false;
6681                    int eapMethod = WifiEnterpriseConfig.Eap.NONE;
6682
6683                    if (targetWificonfiguration != null
6684                            && targetWificonfiguration.enterpriseConfig != null) {
6685                        eapMethod = targetWificonfiguration.enterpriseConfig.getEapMethod();
6686                    }
6687
6688                    // For SIM & AKA/AKA' EAP method Only, get identity from ICC
6689                    if (targetWificonfiguration != null
6690                            && targetWificonfiguration.networkId == networkId
6691                            && targetWificonfiguration.allowedKeyManagement
6692                                    .get(WifiConfiguration.KeyMgmt.IEEE8021X)
6693                            &&  (eapMethod == WifiEnterpriseConfig.Eap.SIM
6694                            || eapMethod == WifiEnterpriseConfig.Eap.AKA
6695                            || eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME)) {
6696                        TelephonyManager tm = (TelephonyManager)
6697                                mContext.getSystemService(Context.TELEPHONY_SERVICE);
6698                        if (tm != null) {
6699                            String imsi = tm.getSubscriberId();
6700                            String mccMnc = "";
6701
6702                            if (tm.getSimState() == TelephonyManager.SIM_STATE_READY)
6703                                 mccMnc = tm.getSimOperator();
6704
6705                            String identity = buildIdentity(eapMethod, imsi, mccMnc);
6706
6707                            if (!identity.isEmpty()) {
6708                                mWifiNative.simIdentityResponse(networkId, identity);
6709                                identitySent = true;
6710                            }
6711                        }
6712                    }
6713                    if (!identitySent) {
6714                        // Supplicant lacks credentials to connect to that network, hence black list
6715                        ssid = (String) message.obj;
6716                        if (targetWificonfiguration != null && ssid != null
6717                                && targetWificonfiguration.SSID != null
6718                                && targetWificonfiguration.SSID.equals("\"" + ssid + "\"")) {
6719                            mWifiConfigStore.handleSSIDStateChange(
6720                                    targetWificonfiguration.networkId, false,
6721                                    "AUTH_FAILED no identity", null);
6722                        }
6723                        // Disconnect now, as we don't have any way to fullfill
6724                        // the  supplicant request.
6725                        mWifiConfigStore.setLastSelectedConfiguration(
6726                                WifiConfiguration.INVALID_NETWORK_ID);
6727                        mWifiNative.disconnect();
6728                    }
6729                    break;
6730                case WifiMonitor.SUP_REQUEST_SIM_AUTH:
6731                    logd("Received SUP_REQUEST_SIM_AUTH");
6732                    SimAuthRequestData requestData = (SimAuthRequestData) message.obj;
6733                    if (requestData != null) {
6734                        if (requestData.protocol == WifiEnterpriseConfig.Eap.SIM) {
6735                            handleGsmAuthRequest(requestData);
6736                        } else if (requestData.protocol == WifiEnterpriseConfig.Eap.AKA
6737                            || requestData.protocol == WifiEnterpriseConfig.Eap.AKA_PRIME) {
6738                            handle3GAuthRequest(requestData);
6739                        }
6740                    } else {
6741                        loge("Invalid sim auth request");
6742                    }
6743                    break;
6744                case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
6745                    replyToMessage(message, message.what,
6746                            mWifiConfigStore.getPrivilegedConfiguredNetworks());
6747                    break;
6748                case CMD_GET_MATCHING_CONFIG:
6749                    replyToMessage(message, message.what,
6750                            mWifiConfigStore.getMatchingConfig((ScanResult)message.obj));
6751                    break;
6752                /* Do a redundant disconnect without transition */
6753                case CMD_DISCONNECT:
6754                    mWifiConfigStore.setLastSelectedConfiguration
6755                            (WifiConfiguration.INVALID_NETWORK_ID);
6756                    mWifiNative.disconnect();
6757                    break;
6758                case CMD_RECONNECT:
6759                    mWifiAutoJoinController.attemptAutoJoin();
6760                    break;
6761                case CMD_REASSOCIATE:
6762                    lastConnectAttempt = System.currentTimeMillis();
6763                    mWifiNative.reassociate();
6764                    break;
6765                case CMD_RELOAD_TLS_AND_RECONNECT:
6766                    if (mWifiConfigStore.needsUnlockedKeyStore()) {
6767                        logd("Reconnecting to give a chance to un-connected TLS networks");
6768                        mWifiNative.disconnect();
6769                        lastConnectAttempt = System.currentTimeMillis();
6770                        mWifiNative.reconnect();
6771                    }
6772                    break;
6773                case CMD_AUTO_ROAM:
6774                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
6775                    return HANDLED;
6776                case CMD_AUTO_CONNECT:
6777                    /* Work Around: wpa_supplicant can get in a bad state where it returns a non
6778                     * associated status to the STATUS command but somehow-someplace still thinks
6779                     * it is associated and thus will ignore select/reconnect command with
6780                     * following message:
6781                     * "Already associated with the selected network - do nothing"
6782                     *
6783                     * Hence, sends a disconnect to supplicant first.
6784                     */
6785                    didDisconnect = false;
6786                    if (getCurrentState() != mDisconnectedState) {
6787                        /** Supplicant will ignore the reconnect if we are currently associated,
6788                         * hence trigger a disconnect
6789                         */
6790                        didDisconnect = true;
6791                        mWifiNative.disconnect();
6792                    }
6793
6794                    /* connect command coming from auto-join */
6795                    config = (WifiConfiguration) message.obj;
6796                    netId = message.arg1;
6797                    int roam = message.arg2;
6798                    loge("CMD_AUTO_CONNECT sup state "
6799                            + mSupplicantStateTracker.getSupplicantStateName()
6800                            + " my state " + getCurrentState().getName()
6801                            + " nid=" + Integer.toString(netId)
6802                            + " roam=" + Integer.toString(roam));
6803                    if (config == null) {
6804                        loge("AUTO_CONNECT and no config, bail out...");
6805                        break;
6806                    }
6807
6808                    /* Make sure we cancel any previous roam request */
6809                    autoRoamSetBSSID(netId, config.BSSID);
6810
6811                    /* Save the network config */
6812                    loge("CMD_AUTO_CONNECT will save config -> " + config.SSID
6813                            + " nid=" + Integer.toString(netId));
6814                    result = mWifiConfigStore.saveNetwork(config, WifiConfiguration.UNKNOWN_UID);
6815                    netId = result.getNetworkId();
6816                    loge("CMD_AUTO_CONNECT did save config -> "
6817                            + " nid=" + Integer.toString(netId));
6818
6819                    if (deferForUserInput(message, netId, false)) {
6820                        break;
6821                    } else if (mWifiConfigStore.getWifiConfiguration(netId).userApproved ==
6822                                                                   WifiConfiguration.USER_BANNED) {
6823                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6824                                WifiManager.NOT_AUTHORIZED);
6825                        break;
6826                    }
6827
6828                    // Make sure the network is enabled, since supplicant will not reenable it
6829                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
6830
6831                    if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ false,
6832                            WifiConfiguration.UNKNOWN_UID) && mWifiNative.reconnect()) {
6833                        lastConnectAttempt = System.currentTimeMillis();
6834                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6835                        config = mWifiConfigStore.getWifiConfiguration(netId);
6836                        if (config != null
6837                                && !mWifiConfigStore.isLastSelectedConfiguration(config)) {
6838                            // If we autojoined a different config than the user selected one,
6839                            // it means we could not see the last user selection,
6840                            // or that the last user selection was faulty and ended up blacklisted
6841                            // for some reason (in which case the user is notified with an error
6842                            // message in the Wifi picker), and thus we managed to auto-join away
6843                            // from the selected  config. -> in that case we need to forget
6844                            // the selection because we don't want to abruptly switch back to it.
6845                            //
6846                            // Note that the user selection is also forgotten after a period of time
6847                            // during which the device has been disconnected.
6848                            // The default value is 30 minutes : see the code path at bottom of
6849                            // setScanResults() function.
6850                            mWifiConfigStore.
6851                                 setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
6852                        }
6853                        mAutoRoaming = roam;
6854                        if (isRoaming() || linkDebouncing) {
6855                            transitionTo(mRoamingState);
6856                        } else if (didDisconnect) {
6857                            transitionTo(mDisconnectingState);
6858                        } else {
6859                            /* Already in disconnected state, nothing to change */
6860                        }
6861                    } else {
6862                        loge("Failed to connect config: " + config + " netId: " + netId);
6863                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6864                                WifiManager.ERROR);
6865                        break;
6866                    }
6867                    break;
6868                case CMD_REMOVE_APP_CONFIGURATIONS:
6869                    mWifiConfigStore.removeNetworksForApp((ApplicationInfo) message.obj);
6870                    break;
6871                case CMD_REMOVE_USER_CONFIGURATIONS:
6872                    mWifiConfigStore.removeNetworksForUser(message.arg1);
6873                    break;
6874                case WifiManager.CONNECT_NETWORK:
6875                    /**
6876                     *  The connect message can contain a network id passed as arg1 on message or
6877                     * or a config passed as obj on message.
6878                     * For a new network, a config is passed to create and connect.
6879                     * For an existing network, a network id is passed
6880                     */
6881                    netId = message.arg1;
6882                    config = (WifiConfiguration) message.obj;
6883                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
6884                    boolean updatedExisting = false;
6885
6886                    /* Save the network config */
6887                    if (config != null) {
6888                        if (!recordUidIfAuthorized(config, message.sendingUid)) {
6889                            loge("Not authorized to update network "
6890                                 + " config=" + config.SSID
6891                                 + " cnid=" + config.networkId
6892                                 + " uid=" + message.sendingUid);
6893                            replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6894                                           WifiManager.NOT_AUTHORIZED);
6895                            break;
6896                        }
6897
6898                        String configKey = config.configKey(true /* allowCached */);
6899                        WifiConfiguration savedConfig =
6900                                mWifiConfigStore.getWifiConfiguration(configKey);
6901                        if (savedConfig != null) {
6902                            // There is an existing config with this netId, but it wasn't exposed
6903                            // (either AUTO_JOIN_DELETED or ephemeral; see WifiConfigStore#
6904                            // getConfiguredNetworks). Remove those bits and update the config.
6905                            config = savedConfig;
6906                            loge("CONNECT_NETWORK updating existing config with id=" +
6907                                    config.networkId + " configKey=" + configKey);
6908                            config.ephemeral = false;
6909                            config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED;
6910                            updatedExisting = true;
6911                        }
6912
6913                        result = mWifiConfigStore.saveNetwork(config, message.sendingUid);
6914                        netId = result.getNetworkId();
6915                    }
6916                    config = mWifiConfigStore.getWifiConfiguration(netId);
6917
6918                    if (config == null) {
6919                        loge("CONNECT_NETWORK no config for id=" + Integer.toString(netId) + " "
6920                                + mSupplicantStateTracker.getSupplicantStateName() + " my state "
6921                                + getCurrentState().getName());
6922                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6923                                WifiManager.ERROR);
6924                        break;
6925                    } else {
6926                        String wasSkipped = config.autoJoinBailedDueToLowRssi ? " skipped" : "";
6927                        loge("CONNECT_NETWORK id=" + Integer.toString(netId)
6928                                + " config=" + config.SSID
6929                                + " cnid=" + config.networkId
6930                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
6931                                + " my state " + getCurrentState().getName()
6932                                + " uid = " + message.sendingUid
6933                                + wasSkipped);
6934                    }
6935
6936                    autoRoamSetBSSID(netId, "any");
6937
6938                    if (message.sendingUid == Process.WIFI_UID
6939                        || message.sendingUid == Process.SYSTEM_UID) {
6940                        // As a sanity measure, clear the BSSID in the supplicant network block.
6941                        // If system or Wifi Settings want to connect, they will not
6942                        // specify the BSSID.
6943                        // If an app however had added a BSSID to this configuration, and the BSSID
6944                        // was wrong, Then we would forever fail to connect until that BSSID
6945                        // is cleaned up.
6946                        clearConfigBSSID(config, "CONNECT_NETWORK");
6947                    }
6948
6949                    if (deferForUserInput(message, netId, true)) {
6950                        break;
6951                    } else if (mWifiConfigStore.getWifiConfiguration(netId).userApproved ==
6952                                                                    WifiConfiguration.USER_BANNED) {
6953                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
6954                                WifiManager.NOT_AUTHORIZED);
6955                        break;
6956                    }
6957
6958                    mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
6959
6960                    /* Tell autojoin the user did try to connect to that network if from settings */
6961                    boolean persist =
6962                        mWifiConfigStore.checkConfigOverridePermission(message.sendingUid);
6963                    mWifiAutoJoinController.updateConfigurationHistory(netId, true, persist);
6964
6965                    mWifiConfigStore.setLastSelectedConfiguration(netId);
6966
6967                    didDisconnect = false;
6968                    if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID
6969                            && mLastNetworkId != netId) {
6970                        /** Supplicant will ignore the reconnect if we are currently associated,
6971                         * hence trigger a disconnect
6972                         */
6973                        didDisconnect = true;
6974                        mWifiNative.disconnect();
6975                    }
6976
6977                    // Make sure the network is enabled, since supplicant will not reenable it
6978                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
6979
6980                    if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ true,
6981                            message.sendingUid) && mWifiNative.reconnect()) {
6982                        lastConnectAttempt = System.currentTimeMillis();
6983                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
6984
6985                        /* The state tracker handles enabling networks upon completion/failure */
6986                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
6987                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
6988                        if (didDisconnect) {
6989                            /* Expect a disconnection from the old connection */
6990                            transitionTo(mDisconnectingState);
6991                        } else if (updatedExisting && getCurrentState() == mConnectedState &&
6992                                getCurrentWifiConfiguration().networkId == netId) {
6993                            // Update the current set of network capabilities, but stay in the
6994                            // current state.
6995                            updateCapabilities(config);
6996                        } else {
6997                            /**
6998                             *  Directly go to disconnected state where we
6999                             * process the connection events from supplicant
7000                             **/
7001                            transitionTo(mDisconnectedState);
7002                        }
7003                    } else {
7004                        loge("Failed to connect config: " + config + " netId: " + netId);
7005                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
7006                                WifiManager.ERROR);
7007                        break;
7008                    }
7009                    break;
7010                case WifiManager.SAVE_NETWORK:
7011                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
7012                    // Fall thru
7013                case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
7014                    lastSavedConfigurationAttempt = null; // Used for debug
7015                    config = (WifiConfiguration) message.obj;
7016                    if (config == null) {
7017                        loge("ERROR: SAVE_NETWORK with null configuration"
7018                                + mSupplicantStateTracker.getSupplicantStateName()
7019                                + " my state " + getCurrentState().getName());
7020                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
7021                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
7022                                WifiManager.ERROR);
7023                        break;
7024                    }
7025                    lastSavedConfigurationAttempt = new WifiConfiguration(config);
7026                    int nid = config.networkId;
7027                    loge("SAVE_NETWORK id=" + Integer.toString(nid)
7028                                + " config=" + config.SSID
7029                                + " nid=" + config.networkId
7030                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
7031                                + " my state " + getCurrentState().getName());
7032
7033                    // Only record the uid if this is user initiated
7034                    boolean checkUid = (message.what == WifiManager.SAVE_NETWORK);
7035                    if (checkUid && !recordUidIfAuthorized(config, message.sendingUid)) {
7036                        loge("Not authorized to update network "
7037                             + " config=" + config.SSID
7038                             + " cnid=" + config.networkId
7039                             + " uid=" + message.sendingUid);
7040                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
7041                                       WifiManager.NOT_AUTHORIZED);
7042                        break;
7043                    }
7044
7045                    result = mWifiConfigStore.saveNetwork(config, WifiConfiguration.UNKNOWN_UID);
7046                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
7047                        if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
7048                            if (result.hasIpChanged()) {
7049                                // The currently connection configuration was changed
7050                                // We switched from DHCP to static or from static to DHCP, or the
7051                                // static IP address has changed.
7052                                log("Reconfiguring IP on connection");
7053                                // TODO: clear addresses and disable IPv6
7054                                // to simplify obtainingIpState.
7055                                transitionTo(mObtainingIpState);
7056                            }
7057                            if (result.hasProxyChanged()) {
7058                                log("Reconfiguring proxy on connection");
7059                                updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
7060                            }
7061                        }
7062                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
7063                        broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
7064
7065                        if (VDBG) {
7066                           loge("Success save network nid="
7067                                    + Integer.toString(result.getNetworkId()));
7068                        }
7069
7070                        synchronized(mScanResultCache) {
7071                            /**
7072                             * If the command comes from WifiManager, then
7073                             * tell autojoin the user did try to modify and save that network,
7074                             * and interpret the SAVE_NETWORK as a request to connect
7075                             */
7076                            boolean user = message.what == WifiManager.SAVE_NETWORK;
7077
7078                            // Did this connect come from settings
7079                            boolean persistConnect =
7080                                mWifiConfigStore.checkConfigOverridePermission(message.sendingUid);
7081
7082                            mWifiAutoJoinController.updateConfigurationHistory(result.getNetworkId()
7083                                    , user, persistConnect);
7084                            mWifiAutoJoinController.attemptAutoJoin();
7085                        }
7086                    } else {
7087                        loge("Failed to save network");
7088                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
7089                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
7090                                WifiManager.ERROR);
7091                    }
7092                    break;
7093                case WifiManager.FORGET_NETWORK:
7094                    // Debug only, remember last configuration that was forgotten
7095                    WifiConfiguration toRemove
7096                            = mWifiConfigStore.getWifiConfiguration(message.arg1);
7097                    if (toRemove == null) {
7098                        lastForgetConfigurationAttempt = null;
7099                    } else {
7100                        lastForgetConfigurationAttempt = new WifiConfiguration(toRemove);
7101                    }
7102                    // check that the caller owns this network
7103                    netId = message.arg1;
7104
7105                    if (!mWifiConfigStore.canModifyNetwork(message.sendingUid, netId)) {
7106                        loge("Not authorized to forget network "
7107                             + " cnid=" + netId
7108                             + " uid=" + message.sendingUid);
7109                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
7110                                WifiManager.NOT_AUTHORIZED);
7111                        break;
7112                    }
7113
7114                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
7115                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
7116                        broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_FORGOT,
7117                                (WifiConfiguration) message.obj);
7118                    } else {
7119                        loge("Failed to forget network");
7120                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
7121                                WifiManager.ERROR);
7122                    }
7123                    break;
7124                case WifiManager.START_WPS:
7125                    WpsInfo wpsInfo = (WpsInfo) message.obj;
7126                    WpsResult wpsResult;
7127                    switch (wpsInfo.setup) {
7128                        case WpsInfo.PBC:
7129                            wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
7130                            break;
7131                        case WpsInfo.KEYPAD:
7132                            wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
7133                            break;
7134                        case WpsInfo.DISPLAY:
7135                            wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
7136                            break;
7137                        default:
7138                            wpsResult = new WpsResult(Status.FAILURE);
7139                            loge("Invalid setup for WPS");
7140                            break;
7141                    }
7142                    mWifiConfigStore.setLastSelectedConfiguration
7143                            (WifiConfiguration.INVALID_NETWORK_ID);
7144                    if (wpsResult.status == Status.SUCCESS) {
7145                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
7146                        transitionTo(mWpsRunningState);
7147                    } else {
7148                        loge("Failed to start WPS with config " + wpsInfo.toString());
7149                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
7150                    }
7151                    break;
7152                case WifiMonitor.NETWORK_CONNECTION_EVENT:
7153                    if (DBG) log("Network connection established");
7154                    mLastNetworkId = message.arg1;
7155                    mLastBssid = (String) message.obj;
7156
7157                    mWifiInfo.setBSSID(mLastBssid);
7158                    mWifiInfo.setNetworkId(mLastNetworkId);
7159
7160                    sendNetworkStateChangeBroadcast(mLastBssid);
7161                    transitionTo(mObtainingIpState);
7162                    break;
7163                case WifiMonitor.ANQP_DONE_EVENT:
7164                    Log.d("HS2J", String.format("WFSM: ANQP for %016x %s",
7165                            (Long)message.obj, message.arg1 != 0 ? "success" : "fail"));
7166                    mWifiConfigStore.notifyANQPDone((Long) message.obj, message.arg1 != 0);
7167                    break;
7168                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7169                    // Calling handleNetworkDisconnect here is redundant because we might already
7170                    // have called it when leaving L2ConnectedState to go to disconnecting state
7171                    // or thru other path
7172                    // We should normally check the mWifiInfo or mLastNetworkId so as to check
7173                    // if they are valid, and only in this case call handleNEtworkDisconnect,
7174                    // TODO: this should be fixed for a L MR release
7175                    // The side effect of calling handleNetworkDisconnect twice is that a bunch of
7176                    // idempotent commands are executed twice (stopping Dhcp, enabling the SPS mode
7177                    // at the chip etc...
7178                    if (DBG) log("ConnectModeState: Network connection lost ");
7179                    handleNetworkDisconnect();
7180                    transitionTo(mDisconnectedState);
7181                    break;
7182                default:
7183                    return NOT_HANDLED;
7184            }
7185            return HANDLED;
7186        }
7187    }
7188
7189    private void updateCapabilities(WifiConfiguration config) {
7190        if (config.ephemeral) {
7191            mNetworkCapabilities.removeCapability(
7192                    NetworkCapabilities.NET_CAPABILITY_TRUSTED);
7193        } else {
7194            mNetworkCapabilities.addCapability(
7195                    NetworkCapabilities.NET_CAPABILITY_TRUSTED);
7196        }
7197        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
7198    }
7199
7200    private class WifiNetworkAgent extends NetworkAgent {
7201        public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
7202                NetworkCapabilities nc, LinkProperties lp, int score) {
7203            super(l, c, TAG, ni, nc, lp, score);
7204        }
7205        protected void unwanted() {
7206            // Ignore if we're not the current networkAgent.
7207            if (this != mNetworkAgent) return;
7208            if (DBG) log("WifiNetworkAgent -> Wifi unwanted score "
7209                    + Integer.toString(mWifiInfo.score));
7210            unwantedNetwork(network_status_unwanted_disconnect);
7211        }
7212
7213        @Override
7214        protected void networkStatus(int status) {
7215            if (this != mNetworkAgent) return;
7216            if (status == NetworkAgent.INVALID_NETWORK) {
7217                if (DBG) log("WifiNetworkAgent -> Wifi networkStatus invalid, score="
7218                        + Integer.toString(mWifiInfo.score));
7219                unwantedNetwork(network_status_unwanted_disable_autojoin);
7220            } else if (status == NetworkAgent.VALID_NETWORK) {
7221                if (DBG && mWifiInfo != null) log("WifiNetworkAgent -> Wifi networkStatus valid, score= "
7222                        + Integer.toString(mWifiInfo.score));
7223                doNetworkStatus(status);
7224            }
7225        }
7226
7227        @Override
7228        protected void saveAcceptUnvalidated(boolean accept) {
7229            if (this != mNetworkAgent) return;
7230            WifiStateMachine.this.sendMessage(CMD_ACCEPT_UNVALIDATED, accept ? 1 : 0);
7231        }
7232    }
7233
7234    void unwantedNetwork(int reason) {
7235        sendMessage(CMD_UNWANTED_NETWORK, reason);
7236    }
7237
7238    void doNetworkStatus(int status) {
7239        sendMessage(CMD_NETWORK_STATUS, status);
7240    }
7241
7242    // rfc4186 & rfc4187:
7243    // create Permanent Identity base on IMSI,
7244    // identity = usernam@realm
7245    // with username = prefix | IMSI
7246    // and realm is derived MMC/MNC tuple according 3GGP spec(TS23.003)
7247    private String buildIdentity(int eapMethod, String imsi, String mccMnc) {
7248        String mcc;
7249        String mnc;
7250        String prefix;
7251
7252        if (imsi == null || imsi.isEmpty())
7253            return "";
7254
7255        if (eapMethod == WifiEnterpriseConfig.Eap.SIM)
7256            prefix = "1";
7257        else if (eapMethod == WifiEnterpriseConfig.Eap.AKA)
7258            prefix = "0";
7259        else if (eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME)
7260            prefix = "6";
7261        else  // not a valide EapMethod
7262            return "";
7263
7264        /* extract mcc & mnc from mccMnc */
7265        if (mccMnc != null && !mccMnc.isEmpty()) {
7266            mcc = mccMnc.substring(0, 3);
7267            mnc = mccMnc.substring(3);
7268            if (mnc.length() == 2)
7269                mnc = "0" + mnc;
7270        } else {
7271            // extract mcc & mnc from IMSI, assume mnc size is 3
7272            mcc = imsi.substring(0, 3);
7273            mnc = imsi.substring(3, 6);
7274        }
7275
7276        return prefix + imsi + "@wlan.mnc" + mnc + ".mcc" + mcc + ".3gppnetwork.org";
7277    }
7278
7279    boolean startScanForConfiguration(WifiConfiguration config, boolean restrictChannelList) {
7280        if (config == null)
7281            return false;
7282
7283        // We are still seeing a fairly high power consumption triggered by autojoin scans
7284        // Hence do partial scans only for PSK configuration that are roamable since the
7285        // primary purpose of the partial scans is roaming.
7286        // Full badn scans with exponential backoff for the purpose or extended roaming and
7287        // network switching are performed unconditionally.
7288        WifiConfigStore.ScanDetailCache scanDetailCache =
7289                mWifiConfigStore.getScanDetailCache(config);
7290        if (scanDetailCache == null
7291                || !config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
7292                || scanDetailCache.size() > 6) {
7293            //return true but to not trigger the scan
7294            return true;
7295        }
7296        HashSet<Integer> channels
7297                = mWifiConfigStore.makeChannelList(config,
7298                ONE_HOUR_MILLI, restrictChannelList);
7299        if (channels != null && channels.size() != 0) {
7300            StringBuilder freqs = new StringBuilder();
7301            boolean first = true;
7302            for (Integer channel : channels) {
7303                if (!first)
7304                    freqs.append(",");
7305                freqs.append(channel.toString());
7306                first = false;
7307            }
7308            //if (DBG) {
7309            loge("WifiStateMachine starting scan for " + config.configKey() + " with " + freqs);
7310            //}
7311            // Call wifi native to start the scan
7312            if (startScanNative(
7313                    WifiNative.SCAN_WITHOUT_CONNECTION_SETUP,
7314                    freqs.toString())) {
7315                // Only count battery consumption if scan request is accepted
7316                noteScanStart(SCAN_ALARM_SOURCE, null);
7317                messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
7318            } else {
7319                // used for debug only, mark scan as failed
7320                messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
7321            }
7322            return true;
7323        } else {
7324            if (DBG) loge("WifiStateMachine no channels for " + config.configKey());
7325            return false;
7326        }
7327    }
7328
7329    void clearCurrentConfigBSSID(String dbg) {
7330        // Clear the bssid in the current config's network block
7331        WifiConfiguration config = getCurrentWifiConfiguration();
7332        if (config == null)
7333            return;
7334        clearConfigBSSID(config, dbg);
7335    }
7336    void clearConfigBSSID(WifiConfiguration config, String dbg) {
7337        if (config == null)
7338            return;
7339        if (DBG) {
7340            loge(dbg + " " + mTargetRoamBSSID + " config " + config.configKey()
7341                    + " config.bssid " + config.BSSID);
7342        }
7343        config.autoJoinBSSID = "any";
7344        config.BSSID = "any";
7345        if (DBG) {
7346           loge(dbg + " " + config.SSID
7347                    + " nid=" + Integer.toString(config.networkId));
7348        }
7349        mWifiConfigStore.saveWifiConfigBSSID(config);
7350    }
7351
7352    class L2ConnectedState extends State {
7353        @Override
7354        public void enter() {
7355            mRssiPollToken++;
7356            if (mEnableRssiPolling) {
7357                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
7358            }
7359            if (mNetworkAgent != null) {
7360                loge("Have NetworkAgent when entering L2Connected");
7361                setNetworkDetailedState(DetailedState.DISCONNECTED);
7362            }
7363            setNetworkDetailedState(DetailedState.CONNECTING);
7364
7365            if (TextUtils.isEmpty(mTcpBufferSizes) == false) {
7366                mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
7367            }
7368            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
7369                    "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,
7370                    mLinkProperties, 60);
7371
7372            // We must clear the config BSSID, as the wifi chipset may decide to roam
7373            // from this point on and having the BSSID specified in the network block would
7374            // cause the roam to faile and the device to disconnect
7375            clearCurrentConfigBSSID("L2ConnectedState");
7376        }
7377
7378        @Override
7379        public void exit() {
7380            // This is handled by receiving a NETWORK_DISCONNECTION_EVENT in ConnectModeState
7381            // Bug: 15347363
7382            // For paranoia's sake, call handleNetworkDisconnect
7383            // only if BSSID is null or last networkId
7384            // is not invalid.
7385            if (DBG) {
7386                StringBuilder sb = new StringBuilder();
7387                sb.append("leaving L2ConnectedState state nid=" + Integer.toString(mLastNetworkId));
7388                if (mLastBssid !=null) {
7389                    sb.append(" ").append(mLastBssid);
7390                }
7391            }
7392            if (mLastBssid != null || mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
7393                handleNetworkDisconnect();
7394            }
7395        }
7396
7397        @Override
7398        public boolean processMessage(Message message) {
7399            logStateAndMessage(message, getClass().getSimpleName());
7400
7401            switch (message.what) {
7402              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
7403                  handlePreDhcpSetup();
7404                  break;
7405              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
7406                  handlePostDhcpSetup();
7407                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
7408                      if (DBG) log("WifiStateMachine DHCP successful");
7409                      handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);
7410                      // We advance to mVerifyingLinkState because handleIPv4Success will call
7411                      // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.
7412                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
7413                      if (DBG) {
7414                          int count = -1;
7415                          WifiConfiguration config = getCurrentWifiConfiguration();
7416                          if (config != null) {
7417                              count = config.numConnectionFailures;
7418                          }
7419                          log("WifiStateMachine DHCP failure count=" + count);
7420                      }
7421                      handleIPv4Failure(DhcpStateMachine.DHCP_FAILURE);
7422                      // As above, we transition to mDisconnectingState via updateLinkProperties.
7423                  }
7424                  break;
7425                case CMD_IP_CONFIGURATION_SUCCESSFUL:
7426                    handleSuccessfulIpConfiguration();
7427                    sendConnectedState();
7428                    transitionTo(mConnectedState);
7429                    break;
7430                case CMD_IP_CONFIGURATION_LOST:
7431                    // Get Link layer stats so as we get fresh tx packet counters
7432                    getWifiLinkLayerStats(true);
7433                    handleIpConfigurationLost();
7434                    transitionTo(mDisconnectingState);
7435                    break;
7436                case CMD_DISCONNECT:
7437                    mWifiNative.disconnect();
7438                    transitionTo(mDisconnectingState);
7439                    break;
7440                case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
7441                    if (message.arg1 == 1) {
7442                        mWifiNative.disconnect();
7443                        mTemporarilyDisconnectWifi = true;
7444                        transitionTo(mDisconnectingState);
7445                    }
7446                    break;
7447                case CMD_SET_OPERATIONAL_MODE:
7448                    if (message.arg1 != CONNECT_MODE) {
7449                        sendMessage(CMD_DISCONNECT);
7450                        deferMessage(message);
7451                        if (message.arg1 == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
7452                            noteWifiDisabledWhileAssociated();
7453                        }
7454                    }
7455                    mWifiConfigStore.
7456                                setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
7457                    break;
7458                case CMD_SET_COUNTRY_CODE:
7459                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7460                    deferMessage(message);
7461                    break;
7462                case CMD_START_SCAN:
7463                    //if (DBG) {
7464                        loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
7465                              + " txSuccessRate="+String.format( "%.2f", mWifiInfo.txSuccessRate)
7466                              + " rxSuccessRate="+String.format( "%.2f", mWifiInfo.rxSuccessRate)
7467                              + " targetRoamBSSID=" + mTargetRoamBSSID
7468                              + " RSSI=" + mWifiInfo.getRssi());
7469                    //}
7470                    if (message.arg1 == SCAN_ALARM_SOURCE) {
7471                        // Check if the CMD_START_SCAN message is obsolete (and thus if it should
7472                        // not be processed) and restart the scan if needed
7473                        boolean shouldScan =
7474                                mScreenOn && mWifiConfigStore.enableAutoJoinScanWhenAssociated.get();
7475                        if (!checkAndRestartDelayedScan(message.arg2,
7476                                shouldScan,
7477                                mWifiConfigStore.associatedPartialScanPeriodMilli.get(), null, null)) {
7478                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
7479                            loge("WifiStateMachine L2Connected CMD_START_SCAN source "
7480                                    + message.arg1
7481                                    + " " + message.arg2 + ", " + mDelayedScanCounter
7482                                    + " -> obsolete");
7483                            return HANDLED;
7484                        }
7485                        if (mP2pConnected.get()) {
7486                            loge("WifiStateMachine L2Connected CMD_START_SCAN source "
7487                                    + message.arg1
7488                                    + " " + message.arg2 + ", " + mDelayedScanCounter
7489                                    + " ignore because P2P is connected");
7490                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7491                            return HANDLED;
7492                        }
7493                        boolean tryFullBandScan = false;
7494                        boolean restrictChannelList = false;
7495                        long now_ms = System.currentTimeMillis();
7496                        if (DBG) {
7497                            loge("WifiStateMachine CMD_START_SCAN with age="
7498                                    + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
7499                                    + " interval=" + fullBandConnectedTimeIntervalMilli
7500                                    + " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
7501                        }
7502                        if (mWifiInfo != null) {
7503                            if (mWifiConfigStore.enableFullBandScanWhenAssociated.get() &&
7504                                    (now_ms - lastFullBandConnectedTimeMilli)
7505                                    > fullBandConnectedTimeIntervalMilli) {
7506                                if (DBG) {
7507                                    loge("WifiStateMachine CMD_START_SCAN try full band scan age="
7508                                         + Long.toString(now_ms - lastFullBandConnectedTimeMilli)
7509                                         + " interval=" + fullBandConnectedTimeIntervalMilli
7510                                         + " maxinterval=" + maxFullBandConnectedTimeIntervalMilli);
7511                                }
7512                                tryFullBandScan = true;
7513                            }
7514
7515                            if (mWifiInfo.txSuccessRate >
7516                                    mWifiConfigStore.maxTxPacketForFullScans
7517                                    || mWifiInfo.rxSuccessRate >
7518                                    mWifiConfigStore.maxRxPacketForFullScans) {
7519                                // Too much traffic at the interface, hence no full band scan
7520                                if (DBG) {
7521                                    loge("WifiStateMachine CMD_START_SCAN " +
7522                                            "prevent full band scan due to pkt rate");
7523                                }
7524                                tryFullBandScan = false;
7525                            }
7526
7527                            if (mWifiInfo.txSuccessRate >
7528                                    mWifiConfigStore.maxTxPacketForPartialScans
7529                                    || mWifiInfo.rxSuccessRate >
7530                                    mWifiConfigStore.maxRxPacketForPartialScans) {
7531                                // Don't scan if lots of packets are being sent
7532                                restrictChannelList = true;
7533                                if (mWifiConfigStore.alwaysEnableScansWhileAssociated.get() == 0) {
7534                                    if (DBG) {
7535                                     loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
7536                                        + " ...and ignore scans"
7537                                        + " tx=" + String.format("%.2f", mWifiInfo.txSuccessRate)
7538                                        + " rx=" + String.format("%.2f", mWifiInfo.rxSuccessRate));
7539                                    }
7540                                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_REFUSED;
7541                                    return HANDLED;
7542                                }
7543                            }
7544                        }
7545
7546                        WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
7547                        if (DBG) {
7548                            loge("WifiStateMachine CMD_START_SCAN full=" +
7549                                    tryFullBandScan);
7550                        }
7551                        if (currentConfiguration != null) {
7552                            if (fullBandConnectedTimeIntervalMilli
7553                                    < mWifiConfigStore.associatedPartialScanPeriodMilli.get()) {
7554                                // Sanity
7555                                fullBandConnectedTimeIntervalMilli
7556                                        = mWifiConfigStore.associatedPartialScanPeriodMilli.get();
7557                            }
7558                            if (tryFullBandScan) {
7559                                lastFullBandConnectedTimeMilli = now_ms;
7560                                if (fullBandConnectedTimeIntervalMilli
7561                                        < mWifiConfigStore.associatedFullScanMaxIntervalMilli) {
7562                                    // Increase the interval
7563                                    fullBandConnectedTimeIntervalMilli
7564                                            = fullBandConnectedTimeIntervalMilli
7565                                            * mWifiConfigStore.associatedFullScanBackoff.get() / 8;
7566
7567                                    if (DBG) {
7568                                        loge("WifiStateMachine CMD_START_SCAN bump interval ="
7569                                        + fullBandConnectedTimeIntervalMilli);
7570                                    }
7571                                }
7572                                handleScanRequest(
7573                                        WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
7574                            } else {
7575                                if (!startScanForConfiguration(
7576                                        currentConfiguration, restrictChannelList)) {
7577                                    if (DBG) {
7578                                        loge("WifiStateMachine starting scan, " +
7579                                                " did not find channels -> full");
7580                                    }
7581                                    lastFullBandConnectedTimeMilli = now_ms;
7582                                    if (fullBandConnectedTimeIntervalMilli
7583                                            < mWifiConfigStore.associatedFullScanMaxIntervalMilli) {
7584                                        // Increase the interval
7585                                        fullBandConnectedTimeIntervalMilli
7586                                                = fullBandConnectedTimeIntervalMilli
7587                                                * mWifiConfigStore.associatedFullScanBackoff.get() / 8;
7588
7589                                        if (DBG) {
7590                                            loge("WifiStateMachine CMD_START_SCAN bump interval ="
7591                                                    + fullBandConnectedTimeIntervalMilli);
7592                                        }
7593                                    }
7594                                    handleScanRequest(
7595                                                WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
7596                                }
7597                            }
7598
7599                        } else {
7600                            loge("CMD_START_SCAN : connected mode and no configuration");
7601                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_HANDLING_ERROR;
7602                        }
7603                    } else {
7604                        // Not scan alarm source
7605                        return NOT_HANDLED;
7606                    }
7607                    break;
7608                    /* Ignore connection to same network */
7609                case WifiManager.CONNECT_NETWORK:
7610                    int netId = message.arg1;
7611                    if (mWifiInfo.getNetworkId() == netId) {
7612                        break;
7613                    }
7614                    return NOT_HANDLED;
7615                    /* Ignore */
7616                case WifiMonitor.NETWORK_CONNECTION_EVENT:
7617                    break;
7618                case CMD_RSSI_POLL:
7619                    if (message.arg1 == mRssiPollToken) {
7620                        if (mWifiConfigStore.enableChipWakeUpWhenAssociated.get()) {
7621                            if (VVDBG) log(" get link layer stats " + mWifiLinkLayerStatsSupported);
7622                            WifiLinkLayerStats stats = getWifiLinkLayerStats(VDBG);
7623                            if (stats != null) {
7624                                // Sanity check the results provided by driver
7625                                if (mWifiInfo.getRssi() != WifiInfo.INVALID_RSSI
7626                                        && (stats.rssi_mgmt == 0
7627                                        || stats.beacon_rx == 0)) {
7628                                    stats = null;
7629                                }
7630                            }
7631                            // Get Info and continue polling
7632                            fetchRssiLinkSpeedAndFrequencyNative();
7633                            calculateWifiScore(stats);
7634                        }
7635                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
7636                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
7637
7638                        if (DBG) sendRssiChangeBroadcast(mWifiInfo.getRssi());
7639                    } else {
7640                        // Polling has completed
7641                    }
7642                    break;
7643                case CMD_ENABLE_RSSI_POLL:
7644                    if (mWifiConfigStore.enableRssiPollWhenAssociated.get()) {
7645                        mEnableRssiPolling = (message.arg1 == 1);
7646                    } else {
7647                        mEnableRssiPolling = false;
7648                    }
7649                    mRssiPollToken++;
7650                    if (mEnableRssiPolling) {
7651                        // First poll
7652                        fetchRssiLinkSpeedAndFrequencyNative();
7653                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
7654                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
7655                    } else {
7656                        cleanWifiScore();
7657                    }
7658                    break;
7659                case WifiManager.RSSI_PKTCNT_FETCH:
7660                    RssiPacketCountInfo info = new RssiPacketCountInfo();
7661                    fetchRssiLinkSpeedAndFrequencyNative();
7662                    info.rssi = mWifiInfo.getRssi();
7663                    fetchPktcntNative(info);
7664                    replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
7665                    break;
7666                case CMD_DELAYED_NETWORK_DISCONNECT:
7667                    if (!linkDebouncing && mWifiConfigStore.enableLinkDebouncing) {
7668
7669                        // Ignore if we are not debouncing
7670                        loge("CMD_DELAYED_NETWORK_DISCONNECT and not debouncing - ignore "
7671                                + message.arg1);
7672                        return HANDLED;
7673                    } else {
7674                        loge("CMD_DELAYED_NETWORK_DISCONNECT and debouncing - disconnect "
7675                                + message.arg1);
7676
7677                        linkDebouncing = false;
7678                        // If we are still debouncing while this message comes,
7679                        // it means we were not able to reconnect within the alloted time
7680                        // = LINK_FLAPPING_DEBOUNCE_MSEC
7681                        // and thus, trigger a real disconnect
7682                        handleNetworkDisconnect();
7683                        transitionTo(mDisconnectedState);
7684                    }
7685                    break;
7686                case CMD_ASSOCIATED_BSSID:
7687                    if ((String) message.obj == null) {
7688                        loge("Associated command w/o BSSID");
7689                        break;
7690                    }
7691                    mLastBssid = (String) message.obj;
7692                    if (mLastBssid != null
7693                            && (mWifiInfo.getBSSID() == null
7694                            || !mLastBssid.equals(mWifiInfo.getBSSID()))) {
7695                        mWifiInfo.setBSSID((String) message.obj);
7696                        sendNetworkStateChangeBroadcast(mLastBssid);
7697                    }
7698                    break;
7699                default:
7700                    return NOT_HANDLED;
7701            }
7702
7703            return HANDLED;
7704        }
7705    }
7706
7707    class ObtainingIpState extends State {
7708        @Override
7709        public void enter() {
7710            if (DBG) {
7711                String key = "";
7712                if (getCurrentWifiConfiguration() != null) {
7713                    key = getCurrentWifiConfiguration().configKey();
7714                }
7715                log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
7716                        + " " + key + " "
7717                        + " roam=" + mAutoRoaming
7718                        + " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId)
7719                        + " watchdog= " + obtainingIpWatchdogCount);
7720            }
7721
7722            // Reset link Debouncing, indicating we have successfully re-connected to the AP
7723            // We might still be roaming
7724            linkDebouncing = false;
7725
7726            // Send event to CM & network change broadcast
7727            setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
7728
7729            // We must clear the config BSSID, as the wifi chipset may decide to roam
7730            // from this point on and having the BSSID specified in the network block would
7731            // cause the roam to faile and the device to disconnect
7732            clearCurrentConfigBSSID("ObtainingIpAddress");
7733
7734            try {
7735                mNwService.enableIpv6(mInterfaceName);
7736            } catch (RemoteException re) {
7737                loge("Failed to enable IPv6: " + re);
7738            } catch (IllegalStateException e) {
7739                loge("Failed to enable IPv6: " + e);
7740            }
7741
7742            if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
7743                if (isRoaming()) {
7744                    renewDhcp();
7745                } else {
7746                    // Remove any IP address on the interface in case we're switching from static
7747                    // IP configuration to DHCP. This is safe because if we get here when not
7748                    // roaming, we don't have a usable address.
7749                    clearIPv4Address(mInterfaceName);
7750                    startDhcp();
7751                }
7752                obtainingIpWatchdogCount++;
7753                loge("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
7754                // Get Link layer stats so as we get fresh tx packet counters
7755                getWifiLinkLayerStats(true);
7756                sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
7757                        obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
7758            } else {
7759                // stop any running dhcp before assigning static IP
7760                stopDhcp();
7761                StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(
7762                        mLastNetworkId);
7763                if (config.ipAddress == null) {
7764                    loge("Static IP lacks address");
7765                    sendMessage(CMD_STATIC_IP_FAILURE);
7766                } else {
7767                    InterfaceConfiguration ifcg = new InterfaceConfiguration();
7768                    ifcg.setLinkAddress(config.ipAddress);
7769                    ifcg.setInterfaceUp();
7770                    try {
7771                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
7772                        if (DBG) log("Static IP configuration succeeded");
7773                        DhcpResults dhcpResults = new DhcpResults(config);
7774                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
7775                    } catch (RemoteException re) {
7776                        loge("Static IP configuration failed: " + re);
7777                        sendMessage(CMD_STATIC_IP_FAILURE);
7778                    } catch (IllegalStateException e) {
7779                        loge("Static IP configuration failed: " + e);
7780                        sendMessage(CMD_STATIC_IP_FAILURE);
7781                    }
7782                }
7783            }
7784        }
7785      @Override
7786      public boolean processMessage(Message message) {
7787          logStateAndMessage(message, getClass().getSimpleName());
7788
7789          switch(message.what) {
7790              case CMD_STATIC_IP_SUCCESS:
7791                  handleIPv4Success((DhcpResults) message.obj, CMD_STATIC_IP_SUCCESS);
7792                  break;
7793              case CMD_STATIC_IP_FAILURE:
7794                  handleIPv4Failure(CMD_STATIC_IP_FAILURE);
7795                  break;
7796              case CMD_AUTO_CONNECT:
7797              case CMD_AUTO_ROAM:
7798                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7799                  break;
7800              case WifiManager.SAVE_NETWORK:
7801              case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
7802                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7803                  deferMessage(message);
7804                  break;
7805                  /* Defer any power mode changes since we must keep active power mode at DHCP */
7806              case CMD_SET_HIGH_PERF_MODE:
7807                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7808                  deferMessage(message);
7809                  break;
7810                  /* Defer scan request since we should not switch to other channels at DHCP */
7811              case CMD_START_SCAN:
7812                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
7813                  deferMessage(message);
7814                  break;
7815              case CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER:
7816                  if (message.arg1 == obtainingIpWatchdogCount) {
7817                      loge("ObtainingIpAddress: Watchdog Triggered, count="
7818                              + obtainingIpWatchdogCount);
7819                      handleIpConfigurationLost();
7820                      transitionTo(mDisconnectingState);
7821                      break;
7822                  }
7823                  messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7824                  break;
7825              default:
7826                  return NOT_HANDLED;
7827          }
7828          return HANDLED;
7829      }
7830    }
7831
7832    class VerifyingLinkState extends State {
7833        @Override
7834        public void enter() {
7835            log(getName() + " enter");
7836            setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
7837            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
7838            sendNetworkStateChangeBroadcast(mLastBssid);
7839            // End roaming
7840            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
7841        }
7842        @Override
7843        public boolean processMessage(Message message) {
7844            logStateAndMessage(message, getClass().getSimpleName());
7845
7846            switch (message.what) {
7847                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7848                    // Stay here
7849                    log(getName() + " POOR_LINK_DETECTED: no transition");
7850                    break;
7851                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
7852                    log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
7853
7854                    log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
7855                    sendConnectedState();
7856                    transitionTo(mConnectedState);
7857                    break;
7858                default:
7859                    if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
7860                    return NOT_HANDLED;
7861            }
7862            return HANDLED;
7863        }
7864    }
7865
7866    private void sendConnectedState() {
7867        // Send out a broadcast with the CAPTIVE_PORTAL_CHECK to preserve
7868        // existing behaviour. The captive portal check really happens after we
7869        // transition into DetailedState.CONNECTED.
7870        setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
7871        mWifiConfigStore.updateStatus(mLastNetworkId,
7872        DetailedState.CAPTIVE_PORTAL_CHECK);
7873        sendNetworkStateChangeBroadcast(mLastBssid);
7874
7875        // If this network was explicitly selected by the user or by an app, evaluate whether to
7876        // call explicitlySelected() so the system can treat it appropriately.
7877        WifiConfiguration config = getCurrentWifiConfiguration();
7878        if (mWifiConfigStore.isLastSelectedConfiguration(config) && mNetworkAgent != null) {
7879            if (mWifiConfigStore.checkConfigOverridePermission(config.lastConnectUid)) {
7880                // Selected by the user via Settings or QuickSettings. If this network has Internet
7881                // access, switch to it. Otherwise, switch to it only if the user confirms that they
7882                // really want to switch, or has already confirmed and selected "Don't ask again".
7883                mNetworkAgent.explicitlySelected(config.noInternetAccessExpected);
7884            } else {
7885                // Selected by an app. Hard switch to this network regardless of whether it has
7886                // Internet access or not.
7887                // TODO: stop doing this, either by requiring apps to update or by putting a
7888                // compatibility hack in place.
7889                mNetworkAgent.explicitlySelected(true);
7890            }
7891        }
7892
7893        setNetworkDetailedState(DetailedState.CONNECTED);
7894        mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
7895        sendNetworkStateChangeBroadcast(mLastBssid);
7896    }
7897
7898    class RoamingState extends State {
7899        boolean mAssociated;
7900        @Override
7901        public void enter() {
7902            if (DBG) {
7903                log("RoamingState Enter"
7904                        + " mScreenOn=" + mScreenOn );
7905            }
7906            setScanAlarm(false);
7907
7908            // Make sure we disconnect if roaming fails
7909            roamWatchdogCount++;
7910            loge("Start Roam Watchdog " + roamWatchdogCount);
7911            sendMessageDelayed(obtainMessage(CMD_ROAM_WATCHDOG_TIMER,
7912                    roamWatchdogCount, 0), ROAM_GUARD_TIMER_MSEC);
7913            mAssociated = false;
7914        }
7915        @Override
7916        public boolean processMessage(Message message) {
7917            logStateAndMessage(message, getClass().getSimpleName());
7918            WifiConfiguration config;
7919            switch (message.what) {
7920                case CMD_IP_CONFIGURATION_LOST:
7921                    config = getCurrentWifiConfiguration();
7922                    if (config != null) {
7923                        mWifiConfigStore.noteRoamingFailure(config,
7924                                WifiConfiguration.ROAMING_FAILURE_IP_CONFIG);
7925                    }
7926                    return NOT_HANDLED;
7927               case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
7928                    if (DBG) log("Roaming and Watchdog reports poor link -> ignore");
7929                    return HANDLED;
7930               case CMD_UNWANTED_NETWORK:
7931                    if (DBG) log("Roaming and CS doesnt want the network -> ignore");
7932                    return HANDLED;
7933               case CMD_SET_OPERATIONAL_MODE:
7934                    if (message.arg1 != CONNECT_MODE) {
7935                        deferMessage(message);
7936                    }
7937                    break;
7938               case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
7939                    /**
7940                     * If we get a SUPPLICANT_STATE_CHANGE_EVENT indicating a DISCONNECT
7941                     * before NETWORK_DISCONNECTION_EVENT
7942                     * And there is an associated BSSID corresponding to our target BSSID, then
7943                     * we have missed the network disconnection, transition to mDisconnectedState
7944                     * and handle the rest of the events there.
7945                     */
7946                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
7947                    if (stateChangeResult.state == SupplicantState.DISCONNECTED
7948                            || stateChangeResult.state == SupplicantState.INACTIVE
7949                            || stateChangeResult.state == SupplicantState.INTERFACE_DISABLED) {
7950                        if (DBG) {
7951                            log("STATE_CHANGE_EVENT in roaming state "
7952                                    + stateChangeResult.toString() );
7953                        }
7954                        if (stateChangeResult.BSSID != null
7955                                && stateChangeResult.BSSID.equals(mTargetRoamBSSID)) {
7956                            handleNetworkDisconnect();
7957                            transitionTo(mDisconnectedState);
7958                        }
7959                    }
7960                    if (stateChangeResult.state == SupplicantState.ASSOCIATED) {
7961                        // We completed the layer2 roaming part
7962                        mAssociated = true;
7963                        if (stateChangeResult.BSSID != null) {
7964                            mTargetRoamBSSID = (String) stateChangeResult.BSSID;
7965                        }
7966                    }
7967                    break;
7968                case CMD_ROAM_WATCHDOG_TIMER:
7969                    if (roamWatchdogCount == message.arg1) {
7970                        if (DBG) log("roaming watchdog! -> disconnect");
7971                        mRoamFailCount++;
7972                        handleNetworkDisconnect();
7973                        mWifiNative.disconnect();
7974                        transitionTo(mDisconnectedState);
7975                    }
7976                    break;
7977               case WifiMonitor.NETWORK_CONNECTION_EVENT:
7978                   if (mAssociated) {
7979                       if (DBG) log("roaming and Network connection established");
7980                       mLastNetworkId = message.arg1;
7981                       mLastBssid = (String) message.obj;
7982                       mWifiInfo.setBSSID(mLastBssid);
7983                       mWifiInfo.setNetworkId(mLastNetworkId);
7984                       mWifiConfigStore.handleBSSIDBlackList(mLastNetworkId, mLastBssid, true);
7985                       sendNetworkStateChangeBroadcast(mLastBssid);
7986                       transitionTo(mObtainingIpState);
7987                   } else {
7988                       messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
7989                   }
7990                   break;
7991               case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
7992                   // Throw away but only if it corresponds to the network we're roaming to
7993                   String bssid = (String)message.obj;
7994                   if (true) {
7995                       String target = "";
7996                       if (mTargetRoamBSSID != null) target = mTargetRoamBSSID;
7997                       log("NETWORK_DISCONNECTION_EVENT in roaming state"
7998                               + " BSSID=" + bssid
7999                               + " target=" + target);
8000                   }
8001                   if (bssid != null && bssid.equals(mTargetRoamBSSID)) {
8002                       handleNetworkDisconnect();
8003                       transitionTo(mDisconnectedState);
8004                   }
8005                   break;
8006                case WifiMonitor.SSID_TEMP_DISABLED:
8007                    // Auth error while roaming
8008                    loge("SSID_TEMP_DISABLED nid=" + Integer.toString(mLastNetworkId)
8009                            + " id=" + Integer.toString(message.arg1)
8010                            + " isRoaming=" + isRoaming()
8011                            + " roam=" + Integer.toString(mAutoRoaming));
8012                    if (message.arg1 == mLastNetworkId) {
8013                        config = getCurrentWifiConfiguration();
8014                        if (config != null) {
8015                            mWifiConfigStore.noteRoamingFailure(config,
8016                                    WifiConfiguration.ROAMING_FAILURE_AUTH_FAILURE);
8017                        }
8018                        handleNetworkDisconnect();
8019                        transitionTo(mDisconnectingState);
8020                    }
8021                    return NOT_HANDLED;
8022                case CMD_START_SCAN:
8023                    deferMessage(message);
8024                    break;
8025                default:
8026                    return NOT_HANDLED;
8027            }
8028            return HANDLED;
8029        }
8030
8031        @Override
8032        public void exit() {
8033            loge("WifiStateMachine: Leaving Roaming state");
8034        }
8035    }
8036
8037    class ConnectedState extends State {
8038        @Override
8039        public void enter() {
8040            String address;
8041            updateDefaultRouteMacAddress(1000);
8042            if (DBG) {
8043                log("ConnectedState Enter "
8044                        + " mScreenOn=" + mScreenOn
8045                        + " scanperiod="
8046                        + Integer.toString(mWifiConfigStore.associatedPartialScanPeriodMilli.get()) );
8047            }
8048            if (mScreenOn
8049                    && mWifiConfigStore.enableAutoJoinScanWhenAssociated.get()) {
8050                // restart scan alarm
8051                startDelayedScan(mWifiConfigStore.associatedPartialScanPeriodMilli.get(), null, null);
8052            }
8053            registerConnected();
8054            lastConnectAttempt = 0;
8055            targetWificonfiguration = null;
8056            // Paranoia
8057            linkDebouncing = false;
8058
8059            // Not roaming anymore
8060            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
8061
8062            if (testNetworkDisconnect) {
8063                testNetworkDisconnectCounter++;
8064                loge("ConnectedState Enter start disconnect test " +
8065                        testNetworkDisconnectCounter);
8066                sendMessageDelayed(obtainMessage(CMD_TEST_NETWORK_DISCONNECT,
8067                        testNetworkDisconnectCounter, 0), 15000);
8068            }
8069
8070            // Reenable all networks, allow for hidden networks to be scanned
8071            mWifiConfigStore.enableAllNetworks();
8072
8073            mLastDriverRoamAttempt = 0;
8074        }
8075        @Override
8076        public boolean processMessage(Message message) {
8077            WifiConfiguration config = null;
8078            logStateAndMessage(message, getClass().getSimpleName());
8079
8080            switch (message.what) {
8081                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
8082                    if (DBG) log("Watchdog reports poor link");
8083                    transitionTo(mVerifyingLinkState);
8084                    break;
8085                case CMD_UNWANTED_NETWORK:
8086                    if (message.arg1 == network_status_unwanted_disconnect) {
8087                        mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
8088                        mWifiNative.disconnect();
8089                        transitionTo(mDisconnectingState);
8090                    } else if (message.arg1 == network_status_unwanted_disable_autojoin) {
8091                        config = getCurrentWifiConfiguration();
8092                        if (config != null) {
8093                            // Disable autojoin
8094                            config.numNoInternetAccessReports += 1;
8095                        }
8096                    }
8097                    return HANDLED;
8098                case CMD_NETWORK_STATUS:
8099                    if (message.arg1 == NetworkAgent.VALID_NETWORK) {
8100                        config = getCurrentWifiConfiguration();
8101                        if (config != null) {
8102                            // re-enable autojoin
8103                            config.numNoInternetAccessReports = 0;
8104                            config.validatedInternetAccess = true;
8105                        }
8106                    }
8107                    return HANDLED;
8108                case CMD_ACCEPT_UNVALIDATED:
8109                    boolean accept = (message.arg1 != 0);
8110                    config = getCurrentWifiConfiguration();
8111                    if (config != null) {
8112                        config.noInternetAccessExpected = accept;
8113                    }
8114                    return HANDLED;
8115                case CMD_TEST_NETWORK_DISCONNECT:
8116                    // Force a disconnect
8117                    if (message.arg1 == testNetworkDisconnectCounter) {
8118                        mWifiNative.disconnect();
8119                    }
8120                    break;
8121                case CMD_ASSOCIATED_BSSID:
8122                    // ASSOCIATING to a new BSSID while already connected, indicates
8123                    // that driver is roaming
8124                    mLastDriverRoamAttempt = System.currentTimeMillis();
8125                    String toBSSID = (String)message.obj;
8126                    if (toBSSID != null && !toBSSID.equals(mWifiInfo.getBSSID())) {
8127                        mWifiConfigStore.driverRoamedFrom(mWifiInfo);
8128                    }
8129                    return NOT_HANDLED;
8130                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
8131                    long lastRoam = 0;
8132                    if (mLastDriverRoamAttempt != 0) {
8133                        // Calculate time since last driver roam attempt
8134                        lastRoam = System.currentTimeMillis() - mLastDriverRoamAttempt;
8135                        mLastDriverRoamAttempt = 0;
8136                    }
8137                    config = getCurrentWifiConfiguration();
8138                    if (mScreenOn
8139                            && !linkDebouncing
8140                            && config != null
8141                            && config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_ENABLED
8142                            && !mWifiConfigStore.isLastSelectedConfiguration(config)
8143                            && (message.arg2 != 3 /* reason cannot be 3, i.e. locally generated */
8144                                || (lastRoam > 0 && lastRoam < 2000) /* unless driver is roaming */)
8145                            && ((ScanResult.is24GHz(mWifiInfo.getFrequency())
8146                                    && mWifiInfo.getRssi() >
8147                                    WifiConfiguration.BAD_RSSI_24)
8148                                    || (ScanResult.is5GHz(mWifiInfo.getFrequency())
8149                                    && mWifiInfo.getRssi() >
8150                                    WifiConfiguration.BAD_RSSI_5))) {
8151                        // Start de-bouncing the L2 disconnection:
8152                        // this L2 disconnection might be spurious.
8153                        // Hence we allow 7 seconds for the state machine to try
8154                        // to reconnect, go thru the
8155                        // roaming cycle and enter Obtaining IP address
8156                        // before signalling the disconnect to ConnectivityService and L3
8157                        startScanForConfiguration(getCurrentWifiConfiguration(), false);
8158                        linkDebouncing = true;
8159
8160                        sendMessageDelayed(obtainMessage(CMD_DELAYED_NETWORK_DISCONNECT,
8161                                0, mLastNetworkId), LINK_FLAPPING_DEBOUNCE_MSEC);
8162                        if (DBG) {
8163                            log("NETWORK_DISCONNECTION_EVENT in connected state"
8164                                    + " BSSID=" + mWifiInfo.getBSSID()
8165                                    + " RSSI=" + mWifiInfo.getRssi()
8166                                    + " freq=" + mWifiInfo.getFrequency()
8167                                    + " reason=" + message.arg2
8168                                    + " -> debounce");
8169                        }
8170                        return HANDLED;
8171                    } else {
8172                        if (DBG) {
8173                            int ajst = -1;
8174                            if (config != null) ajst = config.autoJoinStatus;
8175                            log("NETWORK_DISCONNECTION_EVENT in connected state"
8176                                    + " BSSID=" + mWifiInfo.getBSSID()
8177                                    + " RSSI=" + mWifiInfo.getRssi()
8178                                    + " freq=" + mWifiInfo.getFrequency()
8179                                    + " was debouncing=" + linkDebouncing
8180                                    + " reason=" + message.arg2
8181                                    + " ajst=" + ajst);
8182                        }
8183                    }
8184                    break;
8185                case CMD_AUTO_ROAM:
8186                    // Clear the driver roam indication since we are attempting a framerwork roam
8187                    mLastDriverRoamAttempt = 0;
8188
8189                    /* Connect command coming from auto-join */
8190                    ScanResult candidate = (ScanResult)message.obj;
8191                    String bssid = "any";
8192                    if (candidate != null && candidate.is5GHz()) {
8193                        // Only lock BSSID for 5GHz networks
8194                        bssid = candidate.BSSID;
8195                    }
8196                    int netId = mLastNetworkId;
8197                    config = getCurrentWifiConfiguration();
8198
8199
8200                    if (config == null) {
8201                        loge("AUTO_ROAM and no config, bail out...");
8202                        break;
8203                    }
8204
8205                    loge("CMD_AUTO_ROAM sup state "
8206                            + mSupplicantStateTracker.getSupplicantStateName()
8207                            + " my state " + getCurrentState().getName()
8208                            + " nid=" + Integer.toString(netId)
8209                            + " config " + config.configKey()
8210                            + " roam=" + Integer.toString(message.arg2)
8211                            + " to " + bssid
8212                            + " targetRoamBSSID " + mTargetRoamBSSID);
8213
8214                    /* Save the BSSID so as to lock it @ firmware */
8215                    if (!autoRoamSetBSSID(config, bssid) && !linkDebouncing) {
8216                        loge("AUTO_ROAM nothing to do");
8217                        // Same BSSID, nothing to do
8218                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
8219                        break;
8220                    };
8221
8222                    // Make sure the network is enabled, since supplicant will not reenable it
8223                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
8224
8225                    if (deferForUserInput(message, netId, false)) {
8226                        break;
8227                    } else if (mWifiConfigStore.getWifiConfiguration(netId).userApproved ==
8228                            WifiConfiguration.USER_BANNED) {
8229                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
8230                                WifiManager.NOT_AUTHORIZED);
8231                        break;
8232                    }
8233
8234                    boolean ret = false;
8235                    if (mLastNetworkId != netId) {
8236                       if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ false,
8237                               WifiConfiguration.UNKNOWN_UID) && mWifiNative.reconnect()) {
8238                           ret = true;
8239                       }
8240                    } else {
8241                         ret = mWifiNative.reassociate();
8242                    }
8243                    if (ret) {
8244                        lastConnectAttempt = System.currentTimeMillis();
8245                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
8246
8247                        // replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
8248                        mAutoRoaming = message.arg2;
8249                        transitionTo(mRoamingState);
8250
8251                    } else {
8252                        loge("Failed to connect config: " + config + " netId: " + netId);
8253                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
8254                                WifiManager.ERROR);
8255                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
8256                        break;
8257                    }
8258                    break;
8259                default:
8260                    return NOT_HANDLED;
8261            }
8262            return HANDLED;
8263        }
8264
8265        @Override
8266        public void exit() {
8267            loge("WifiStateMachine: Leaving Connected state");
8268            setScanAlarm(false);
8269            mLastDriverRoamAttempt = 0;
8270        }
8271    }
8272
8273    class DisconnectingState extends State {
8274
8275        @Override
8276        public void enter() {
8277
8278            if (PDBG) {
8279                loge(" Enter DisconnectingState State scan interval " + mFrameworkScanIntervalMs
8280                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
8281                        + " screenOn=" + mScreenOn);
8282            }
8283
8284            // Make sure we disconnect: we enter this state prior connecting to a new
8285            // network, waiting for either a DISCONECT event or a SUPPLICANT_STATE_CHANGE
8286            // event which in this case will be indicating that supplicant started to associate.
8287            // In some cases supplicant doesn't ignore the connect requests (it might not
8288            // find the target SSID in its cache),
8289            // Therefore we end up stuck that state, hence the need for the watchdog.
8290            disconnectingWatchdogCount++;
8291            loge("Start Disconnecting Watchdog " + disconnectingWatchdogCount);
8292            sendMessageDelayed(obtainMessage(CMD_DISCONNECTING_WATCHDOG_TIMER,
8293                    disconnectingWatchdogCount, 0), DISCONNECTING_GUARD_TIMER_MSEC);
8294        }
8295
8296        @Override
8297        public boolean processMessage(Message message) {
8298            logStateAndMessage(message, getClass().getSimpleName());
8299            switch (message.what) {
8300                case CMD_SET_OPERATIONAL_MODE:
8301                    if (message.arg1 != CONNECT_MODE) {
8302                        deferMessage(message);
8303                    }
8304                    break;
8305                case CMD_START_SCAN:
8306                    deferMessage(message);
8307                    return HANDLED;
8308                case CMD_DISCONNECTING_WATCHDOG_TIMER:
8309                    if (disconnectingWatchdogCount == message.arg1) {
8310                        if (DBG) log("disconnecting watchdog! -> disconnect");
8311                        handleNetworkDisconnect();
8312                        transitionTo(mDisconnectedState);
8313                    }
8314                    break;
8315                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
8316                    /**
8317                     * If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
8318                     * we have missed the network disconnection, transition to mDisconnectedState
8319                     * and handle the rest of the events there
8320                     */
8321                    deferMessage(message);
8322                    handleNetworkDisconnect();
8323                    transitionTo(mDisconnectedState);
8324                    break;
8325                default:
8326                    return NOT_HANDLED;
8327            }
8328            return HANDLED;
8329        }
8330    }
8331
8332    class DisconnectedState extends State {
8333        @Override
8334        public void enter() {
8335            // We dont scan frequently if this is a temporary disconnect
8336            // due to p2p
8337            if (mTemporarilyDisconnectWifi) {
8338                mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
8339                return;
8340            }
8341
8342            mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
8343                    Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
8344                    mDefaultFrameworkScanIntervalMs);
8345
8346            if (PDBG) {
8347                loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs
8348                        + " mEnableBackgroundScan= " + mEnableBackgroundScan
8349                        + " screenOn=" + mScreenOn
8350                        + " mFrameworkScanIntervalMs=" + mFrameworkScanIntervalMs);
8351            }
8352
8353            /** clear the roaming state, if we were roaming, we failed */
8354            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
8355
8356            if (mScreenOn) {
8357                /**
8358                 * screen lit and => delayed timer
8359                 */
8360                startDelayedScan(mDisconnectedScanPeriodMs, null, null);
8361            } else {
8362                /**
8363                 * screen dark and PNO supported => scan alarm disabled
8364                 */
8365                if (mEnableBackgroundScan) {
8366                    /* If a regular scan result is pending, do not initiate background
8367                     * scan until the scan results are returned. This is needed because
8368                     * initiating a background scan will cancel the regular scan and
8369                     * scan results will not be returned until background scanning is
8370                     * cleared
8371                     */
8372                    if (!mIsScanOngoing) {
8373                        enableBackgroundScan(true);
8374                    }
8375                } else {
8376                    setScanAlarm(true);
8377                }
8378            }
8379
8380            /**
8381             * If we have no networks saved, the supplicant stops doing the periodic scan.
8382             * The scans are useful to notify the user of the presence of an open network.
8383             * Note that these are not wake up scans.
8384             */
8385            if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) {
8386                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8387                        ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8388            }
8389
8390            mDisconnectedTimeStamp = System.currentTimeMillis();
8391
8392        }
8393        @Override
8394        public boolean processMessage(Message message) {
8395            boolean ret = HANDLED;
8396
8397            logStateAndMessage(message, getClass().getSimpleName());
8398
8399            switch (message.what) {
8400                case CMD_NO_NETWORKS_PERIODIC_SCAN:
8401                    if (mP2pConnected.get()) break;
8402                    if (message.arg1 == mPeriodicScanToken &&
8403                            mWifiConfigStore.getConfiguredNetworks().size() == 0) {
8404                        startScan(UNKNOWN_SCAN_SOURCE, -1, null, null);
8405                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8406                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8407                    }
8408                    break;
8409                case WifiManager.FORGET_NETWORK:
8410                case CMD_REMOVE_NETWORK:
8411                case CMD_REMOVE_APP_CONFIGURATIONS:
8412                case CMD_REMOVE_USER_CONFIGURATIONS:
8413                    // Set up a delayed message here. After the forget/remove is handled
8414                    // the handled delayed message will determine if there is a need to
8415                    // scan and continue
8416                    sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8417                                ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8418                    ret = NOT_HANDLED;
8419                    break;
8420                case CMD_SET_OPERATIONAL_MODE:
8421                    if (message.arg1 != CONNECT_MODE) {
8422                        mOperationalMode = message.arg1;
8423
8424                        mWifiConfigStore.disableAllNetworks();
8425                        if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
8426                            mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
8427                            setWifiState(WIFI_STATE_DISABLED);
8428                        }
8429                        transitionTo(mScanModeState);
8430                    }
8431                    mWifiConfigStore.
8432                            setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
8433                    break;
8434                    /* Ignore network disconnect */
8435                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
8436                    break;
8437                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
8438                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
8439                    if (DBG) {
8440                        loge("SUPPLICANT_STATE_CHANGE_EVENT state=" + stateChangeResult.state +
8441                                " -> state= " + WifiInfo.getDetailedStateOf(stateChangeResult.state)
8442                                + " debouncing=" + linkDebouncing);
8443                    }
8444                    setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
8445                    /* ConnectModeState does the rest of the handling */
8446                    ret = NOT_HANDLED;
8447                    break;
8448                case CMD_START_SCAN:
8449                    if (!checkOrDeferScanAllowed(message)) {
8450                        // The scan request was rescheduled
8451                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_REFUSED;
8452                        return HANDLED;
8453                    }
8454                    /* Disable background scan temporarily during a regular scan */
8455                    if (mEnableBackgroundScan) {
8456                        enableBackgroundScan(false);
8457                    }
8458                    if (message.arg1 == SCAN_ALARM_SOURCE) {
8459                        // Check if the CMD_START_SCAN message is obsolete (and thus if it should
8460                        // not be processed) and restart the scan
8461                        int period =  mDisconnectedScanPeriodMs;
8462                        if (mP2pConnected.get()) {
8463                           period = (int)Settings.Global.getLong(mContext.getContentResolver(),
8464                                    Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
8465                                    mDisconnectedScanPeriodMs);
8466                        }
8467                        if (!checkAndRestartDelayedScan(message.arg2,
8468                                true, period, null, null)) {
8469                            messageHandlingStatus = MESSAGE_HANDLING_STATUS_OBSOLETE;
8470                            loge("WifiStateMachine Disconnected CMD_START_SCAN source "
8471                                    + message.arg1
8472                                    + " " + message.arg2 + ", " + mDelayedScanCounter
8473                                    + " -> obsolete");
8474                            return HANDLED;
8475                        }
8476                        handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
8477                        ret = HANDLED;
8478                    } else {
8479                        ret = NOT_HANDLED;
8480                    }
8481                    break;
8482                case WifiMonitor.SCAN_RESULTS_EVENT:
8483                    /* Re-enable background scan when a pending scan result is received */
8484                    if (mEnableBackgroundScan && mIsScanOngoing) {
8485                        enableBackgroundScan(true);
8486                    }
8487                    /* Handled in parent state */
8488                    ret = NOT_HANDLED;
8489                    break;
8490                case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
8491                    NetworkInfo info = (NetworkInfo) message.obj;
8492                    mP2pConnected.set(info.isConnected());
8493                    if (mP2pConnected.get()) {
8494                        int defaultInterval = mContext.getResources().getInteger(
8495                                R.integer.config_wifi_scan_interval_p2p_connected);
8496                        long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
8497                                Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
8498                                defaultInterval);
8499                        mWifiNative.setScanInterval((int) scanIntervalMs/1000);
8500                    } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
8501                        if (DBG) log("Turn on scanning after p2p disconnected");
8502                        sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
8503                                    ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
8504                    } else {
8505                        // If P2P is not connected and there are saved networks, then restart
8506                        // scanning at the normal period. This is necessary because scanning might
8507                        // have been disabled altogether if WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS
8508                        // was set to zero.
8509                        startDelayedScan(mDisconnectedScanPeriodMs, null, null);
8510                    }
8511                    break;
8512                case CMD_RECONNECT:
8513                case CMD_REASSOCIATE:
8514                    if (mTemporarilyDisconnectWifi) {
8515                        // Drop a third party reconnect/reassociate if STA is
8516                        // temporarily disconnected for p2p
8517                        break;
8518                    } else {
8519                        // ConnectModeState handles it
8520                        ret = NOT_HANDLED;
8521                    }
8522                    break;
8523                case CMD_SCREEN_STATE_CHANGED:
8524                    handleScreenStateChanged(message.arg1 != 0,
8525                            /* startBackgroundScanIfNeeded = */ true);
8526                    break;
8527                default:
8528                    ret = NOT_HANDLED;
8529            }
8530            return ret;
8531        }
8532
8533        @Override
8534        public void exit() {
8535            /* No need for a background scan upon exit from a disconnected state */
8536            if (mEnableBackgroundScan) {
8537                enableBackgroundScan(false);
8538            }
8539            setScanAlarm(false);
8540        }
8541    }
8542
8543    class WpsRunningState extends State {
8544        // Tracks the source to provide a reply
8545        private Message mSourceMessage;
8546        @Override
8547        public void enter() {
8548            mSourceMessage = Message.obtain(getCurrentMessage());
8549        }
8550        @Override
8551        public boolean processMessage(Message message) {
8552            logStateAndMessage(message, getClass().getSimpleName());
8553
8554            switch (message.what) {
8555                case WifiMonitor.WPS_SUCCESS_EVENT:
8556                    // Ignore intermediate success, wait for full connection
8557                    break;
8558                case WifiMonitor.NETWORK_CONNECTION_EVENT:
8559                    replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
8560                    mSourceMessage.recycle();
8561                    mSourceMessage = null;
8562                    deferMessage(message);
8563                    transitionTo(mDisconnectedState);
8564                    break;
8565                case WifiMonitor.WPS_OVERLAP_EVENT:
8566                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
8567                            WifiManager.WPS_OVERLAP_ERROR);
8568                    mSourceMessage.recycle();
8569                    mSourceMessage = null;
8570                    transitionTo(mDisconnectedState);
8571                    break;
8572                case WifiMonitor.WPS_FAIL_EVENT:
8573                    // Arg1 has the reason for the failure
8574                    if ((message.arg1 != WifiManager.ERROR) || (message.arg2 != 0)) {
8575                        replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, message.arg1);
8576                        mSourceMessage.recycle();
8577                        mSourceMessage = null;
8578                        transitionTo(mDisconnectedState);
8579                    } else {
8580                        if (DBG) log("Ignore unspecified fail event during WPS connection");
8581                    }
8582                    break;
8583                case WifiMonitor.WPS_TIMEOUT_EVENT:
8584                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
8585                            WifiManager.WPS_TIMED_OUT);
8586                    mSourceMessage.recycle();
8587                    mSourceMessage = null;
8588                    transitionTo(mDisconnectedState);
8589                    break;
8590                case WifiManager.START_WPS:
8591                    replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
8592                    break;
8593                case WifiManager.CANCEL_WPS:
8594                    if (mWifiNative.cancelWps()) {
8595                        replyToMessage(message, WifiManager.CANCEL_WPS_SUCCEDED);
8596                    } else {
8597                        replyToMessage(message, WifiManager.CANCEL_WPS_FAILED, WifiManager.ERROR);
8598                    }
8599                    transitionTo(mDisconnectedState);
8600                    break;
8601                /**
8602                 * Defer all commands that can cause connections to a different network
8603                 * or put the state machine out of connect mode
8604                 */
8605                case CMD_STOP_DRIVER:
8606                case CMD_SET_OPERATIONAL_MODE:
8607                case WifiManager.CONNECT_NETWORK:
8608                case CMD_ENABLE_NETWORK:
8609                case CMD_RECONNECT:
8610                case CMD_REASSOCIATE:
8611                case CMD_ENABLE_ALL_NETWORKS:
8612                    deferMessage(message);
8613                    break;
8614                case CMD_AUTO_CONNECT:
8615                case CMD_AUTO_ROAM:
8616                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
8617                    return HANDLED;
8618                case CMD_START_SCAN:
8619                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
8620                    return HANDLED;
8621                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
8622                    if (DBG) log("Network connection lost");
8623                    handleNetworkDisconnect();
8624                    break;
8625                case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
8626                    if (DBG) log("Ignore Assoc reject event during WPS Connection");
8627                    break;
8628                case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
8629                    // Disregard auth failure events during WPS connection. The
8630                    // EAP sequence is retried several times, and there might be
8631                    // failures (especially for wps pin). We will get a WPS_XXX
8632                    // event at the end of the sequence anyway.
8633                    if (DBG) log("Ignore auth failure during WPS connection");
8634                    break;
8635                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
8636                    // Throw away supplicant state changes when WPS is running.
8637                    // We will start getting supplicant state changes once we get
8638                    // a WPS success or failure
8639                    break;
8640                default:
8641                    return NOT_HANDLED;
8642            }
8643            return HANDLED;
8644        }
8645
8646        @Override
8647        public void exit() {
8648            mWifiConfigStore.enableAllNetworks();
8649            mWifiConfigStore.loadConfiguredNetworks();
8650        }
8651    }
8652
8653    class SoftApStartingState extends State {
8654        @Override
8655        public void enter() {
8656            final Message message = getCurrentMessage();
8657            if (message.what == CMD_START_AP) {
8658                final WifiConfiguration config = (WifiConfiguration) message.obj;
8659
8660                if (config == null) {
8661                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
8662                } else {
8663                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
8664                    startSoftApWithConfig(config);
8665                }
8666            } else {
8667                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
8668            }
8669        }
8670        @Override
8671        public boolean processMessage(Message message) {
8672            logStateAndMessage(message, getClass().getSimpleName());
8673
8674            switch(message.what) {
8675                case CMD_START_SUPPLICANT:
8676                case CMD_STOP_SUPPLICANT:
8677                case CMD_START_AP:
8678                case CMD_STOP_AP:
8679                case CMD_START_DRIVER:
8680                case CMD_STOP_DRIVER:
8681                case CMD_SET_OPERATIONAL_MODE:
8682                case CMD_SET_COUNTRY_CODE:
8683                case CMD_SET_FREQUENCY_BAND:
8684                case CMD_START_PACKET_FILTERING:
8685                case CMD_STOP_PACKET_FILTERING:
8686                case CMD_TETHER_STATE_CHANGE:
8687                    deferMessage(message);
8688                    break;
8689                case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
8690                    WifiConfiguration config = (WifiConfiguration) message.obj;
8691                    if (config != null) {
8692                        startSoftApWithConfig(config);
8693                    } else {
8694                        loge("Softap config is null!");
8695                        sendMessage(CMD_START_AP_FAILURE);
8696                    }
8697                    break;
8698                case CMD_START_AP_SUCCESS:
8699                    setWifiApState(WIFI_AP_STATE_ENABLED);
8700                    transitionTo(mSoftApStartedState);
8701                    break;
8702                case CMD_START_AP_FAILURE:
8703                    setWifiApState(WIFI_AP_STATE_FAILED);
8704                    transitionTo(mInitialState);
8705                    break;
8706                default:
8707                    return NOT_HANDLED;
8708            }
8709            return HANDLED;
8710        }
8711    }
8712
8713    class SoftApStartedState extends State {
8714        @Override
8715        public boolean processMessage(Message message) {
8716            logStateAndMessage(message, getClass().getSimpleName());
8717
8718            switch(message.what) {
8719                case CMD_STOP_AP:
8720                    if (DBG) log("Stopping Soft AP");
8721                    /* We have not tethered at this point, so we just shutdown soft Ap */
8722                    try {
8723                        mNwService.stopAccessPoint(mInterfaceName);
8724                    } catch(Exception e) {
8725                        loge("Exception in stopAccessPoint()");
8726                    }
8727                    setWifiApState(WIFI_AP_STATE_DISABLED);
8728                    transitionTo(mInitialState);
8729                    break;
8730                case CMD_START_AP:
8731                    // Ignore a start on a running access point
8732                    break;
8733                    // Fail client mode operation when soft AP is enabled
8734                case CMD_START_SUPPLICANT:
8735                    loge("Cannot start supplicant with a running soft AP");
8736                    setWifiState(WIFI_STATE_UNKNOWN);
8737                    break;
8738                case CMD_TETHER_STATE_CHANGE:
8739                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8740                    if (startTethering(stateChange.available)) {
8741                        transitionTo(mTetheringState);
8742                    }
8743                    break;
8744                default:
8745                    return NOT_HANDLED;
8746            }
8747            return HANDLED;
8748        }
8749    }
8750
8751    class TetheringState extends State {
8752        @Override
8753        public void enter() {
8754            /* Send ourselves a delayed message to shut down if tethering fails to notify */
8755            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
8756                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
8757        }
8758        @Override
8759        public boolean processMessage(Message message) {
8760            logStateAndMessage(message, getClass().getSimpleName());
8761
8762            switch(message.what) {
8763                case CMD_TETHER_STATE_CHANGE:
8764                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8765                    if (isWifiTethered(stateChange.active)) {
8766                        transitionTo(mTetheredState);
8767                    }
8768                    return HANDLED;
8769                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
8770                    if (message.arg1 == mTetherToken) {
8771                        loge("Failed to get tether update, shutdown soft access point");
8772                        transitionTo(mSoftApStartedState);
8773                        // Needs to be first thing handled
8774                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
8775                    }
8776                    break;
8777                case CMD_START_SUPPLICANT:
8778                case CMD_STOP_SUPPLICANT:
8779                case CMD_START_AP:
8780                case CMD_STOP_AP:
8781                case CMD_START_DRIVER:
8782                case CMD_STOP_DRIVER:
8783                case CMD_SET_OPERATIONAL_MODE:
8784                case CMD_SET_COUNTRY_CODE:
8785                case CMD_SET_FREQUENCY_BAND:
8786                case CMD_START_PACKET_FILTERING:
8787                case CMD_STOP_PACKET_FILTERING:
8788                    deferMessage(message);
8789                    break;
8790                default:
8791                    return NOT_HANDLED;
8792            }
8793            return HANDLED;
8794        }
8795    }
8796
8797    class TetheredState extends State {
8798        @Override
8799        public boolean processMessage(Message message) {
8800            logStateAndMessage(message, getClass().getSimpleName());
8801
8802            switch(message.what) {
8803                case CMD_TETHER_STATE_CHANGE:
8804                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8805                    if (!isWifiTethered(stateChange.active)) {
8806                        loge("Tethering reports wifi as untethered!, shut down soft Ap");
8807                        setHostApRunning(null, false);
8808                        setHostApRunning(null, true);
8809                    }
8810                    return HANDLED;
8811                case CMD_STOP_AP:
8812                    if (DBG) log("Untethering before stopping AP");
8813                    setWifiApState(WIFI_AP_STATE_DISABLING);
8814                    stopTethering();
8815                    transitionTo(mUntetheringState);
8816                    // More work to do after untethering
8817                    deferMessage(message);
8818                    break;
8819                default:
8820                    return NOT_HANDLED;
8821            }
8822            return HANDLED;
8823        }
8824    }
8825
8826    class UntetheringState extends State {
8827        @Override
8828        public void enter() {
8829            /* Send ourselves a delayed message to shut down if tethering fails to notify */
8830            sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
8831                    ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
8832
8833        }
8834        @Override
8835        public boolean processMessage(Message message) {
8836            logStateAndMessage(message, getClass().getSimpleName());
8837
8838            switch(message.what) {
8839                case CMD_TETHER_STATE_CHANGE:
8840                    TetherStateChange stateChange = (TetherStateChange) message.obj;
8841
8842                    /* Wait till wifi is untethered */
8843                    if (isWifiTethered(stateChange.active)) break;
8844
8845                    transitionTo(mSoftApStartedState);
8846                    break;
8847                case CMD_TETHER_NOTIFICATION_TIMED_OUT:
8848                    if (message.arg1 == mTetherToken) {
8849                        loge("Failed to get tether update, force stop access point");
8850                        transitionTo(mSoftApStartedState);
8851                    }
8852                    break;
8853                case CMD_START_SUPPLICANT:
8854                case CMD_STOP_SUPPLICANT:
8855                case CMD_START_AP:
8856                case CMD_STOP_AP:
8857                case CMD_START_DRIVER:
8858                case CMD_STOP_DRIVER:
8859                case CMD_SET_OPERATIONAL_MODE:
8860                case CMD_SET_COUNTRY_CODE:
8861                case CMD_SET_FREQUENCY_BAND:
8862                case CMD_START_PACKET_FILTERING:
8863                case CMD_STOP_PACKET_FILTERING:
8864                    deferMessage(message);
8865                    break;
8866                default:
8867                    return NOT_HANDLED;
8868            }
8869            return HANDLED;
8870        }
8871    }
8872
8873    /**
8874     * State machine initiated requests can have replyTo set to null indicating
8875     * there are no recepients, we ignore those reply actions.
8876     */
8877    private void replyToMessage(Message msg, int what) {
8878        if (msg.replyTo == null) return;
8879        Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
8880        mReplyChannel.replyToMessage(msg, dstMsg);
8881    }
8882
8883    private void replyToMessage(Message msg, int what, int arg1) {
8884        if (msg.replyTo == null) return;
8885        Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
8886        dstMsg.arg1 = arg1;
8887        mReplyChannel.replyToMessage(msg, dstMsg);
8888    }
8889
8890    private void replyToMessage(Message msg, int what, Object obj) {
8891        if (msg.replyTo == null) return;
8892        Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
8893        dstMsg.obj = obj;
8894        mReplyChannel.replyToMessage(msg, dstMsg);
8895    }
8896
8897    /**
8898     * arg2 on the source message has a unique id that needs to be retained in replies
8899     * to match the request
8900     * <p>see WifiManager for details
8901     */
8902    private Message obtainMessageWithWhatAndArg2(Message srcMsg, int what) {
8903        Message msg = Message.obtain();
8904        msg.what = what;
8905        msg.arg2 = srcMsg.arg2;
8906        return msg;
8907    }
8908
8909    /**
8910     * @param wifiCredentialEventType WIFI_CREDENTIAL_SAVED or WIFI_CREDENTIAL_FORGOT
8911     * @param msg Must have a WifiConfiguration obj to succeed
8912     */
8913    private void broadcastWifiCredentialChanged(int wifiCredentialEventType,
8914            WifiConfiguration config) {
8915        if (config != null && config.preSharedKey != null) {
8916            Intent intent = new Intent(WifiManager.WIFI_CREDENTIAL_CHANGED_ACTION);
8917            intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_SSID, config.SSID);
8918            intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_EVENT_TYPE,
8919                    wifiCredentialEventType);
8920            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT,
8921                    android.Manifest.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE);
8922        }
8923    }
8924
8925    private static int parseHex(char ch) {
8926        if ('0' <= ch && ch <= '9') {
8927            return ch - '0';
8928        } else if ('a' <= ch && ch <= 'f') {
8929            return ch - 'a' + 10;
8930        } else if ('A' <= ch && ch <= 'F') {
8931            return ch - 'A' + 10;
8932        } else {
8933            throw new NumberFormatException("" + ch + " is not a valid hex digit");
8934        }
8935    }
8936
8937    private byte[] parseHex(String hex) {
8938        /* This only works for good input; don't throw bad data at it */
8939        if (hex == null) {
8940            return new byte[0];
8941        }
8942
8943        if (hex.length() % 2 != 0) {
8944            throw new NumberFormatException(hex + " is not a valid hex string");
8945        }
8946
8947        byte[] result = new byte[(hex.length())/2 + 1];
8948        result[0] = (byte) ((hex.length())/2);
8949        for (int i = 0, j = 1; i < hex.length(); i += 2, j++) {
8950            int val = parseHex(hex.charAt(i)) * 16 + parseHex(hex.charAt(i+1));
8951            byte b = (byte) (val & 0xFF);
8952            result[j] = b;
8953        }
8954
8955        return result;
8956    }
8957
8958    private static String makeHex(byte[] bytes) {
8959        StringBuilder sb = new StringBuilder();
8960        for (byte b : bytes) {
8961            sb.append(String.format("%02x", b));
8962        }
8963        return sb.toString();
8964    }
8965
8966    private static String makeHex(byte[] bytes, int from, int len) {
8967        StringBuilder sb = new StringBuilder();
8968        for (int i = 0; i < len; i++) {
8969            sb.append(String.format("%02x", bytes[from+i]));
8970        }
8971        return sb.toString();
8972    }
8973
8974    private static byte[] concat(byte[] array1, byte[] array2, byte[] array3) {
8975
8976        int len = array1.length + array2.length + array3.length;
8977
8978        if (array1.length != 0) {
8979            len++;                      /* add another byte for size */
8980        }
8981
8982        if (array2.length != 0) {
8983            len++;                      /* add another byte for size */
8984        }
8985
8986        if (array3.length != 0) {
8987            len++;                      /* add another byte for size */
8988        }
8989
8990        byte[] result = new byte[len];
8991
8992        int index = 0;
8993        if (array1.length != 0) {
8994            result[index] = (byte) (array1.length & 0xFF);
8995            index++;
8996            for (byte b : array1) {
8997                result[index] = b;
8998                index++;
8999            }
9000        }
9001
9002        if (array2.length != 0) {
9003            result[index] = (byte) (array2.length & 0xFF);
9004            index++;
9005            for (byte b : array2) {
9006                result[index] = b;
9007                index++;
9008            }
9009        }
9010
9011        if (array3.length != 0) {
9012            result[index] = (byte) (array3.length & 0xFF);
9013            index++;
9014            for (byte b : array3) {
9015                result[index] = b;
9016                index++;
9017            }
9018        }
9019        return result;
9020    }
9021
9022    private static byte[] concatHex(byte[] array1, byte[] array2) {
9023
9024        int len = array1.length + array2.length;
9025
9026        byte[] result = new byte[len];
9027
9028        int index = 0;
9029        if (array1.length != 0) {
9030            for (byte b : array1) {
9031                result[index] = b;
9032                index++;
9033            }
9034        }
9035
9036        if (array2.length != 0) {
9037            for (byte b : array2) {
9038                result[index] = b;
9039                index++;
9040            }
9041        }
9042
9043        return result;
9044    }
9045
9046    void handleGsmAuthRequest(SimAuthRequestData requestData) {
9047        if (targetWificonfiguration == null
9048                || targetWificonfiguration.networkId == requestData.networkId) {
9049            logd("id matches targetWifiConfiguration");
9050        } else {
9051            logd("id does not match targetWifiConfiguration");
9052            return;
9053        }
9054
9055        TelephonyManager tm = (TelephonyManager)
9056                mContext.getSystemService(Context.TELEPHONY_SERVICE);
9057
9058        if (tm != null) {
9059            StringBuilder sb = new StringBuilder();
9060            for (String challenge : requestData.data) {
9061
9062                if (challenge == null || challenge.isEmpty())
9063                    continue;
9064                logd("RAND = " + challenge);
9065
9066                byte[] rand = null;
9067                try {
9068                    rand = parseHex(challenge);
9069                } catch (NumberFormatException e) {
9070                    loge("malformed challenge");
9071                    continue;
9072                }
9073
9074                String base64Challenge = android.util.Base64.encodeToString(
9075                        rand, android.util.Base64.NO_WRAP);
9076                /*
9077                 * First, try with appType = 2 => USIM according to
9078                 * com.android.internal.telephony.PhoneConstants#APPTYPE_xxx
9079                 */
9080                int appType = 2;
9081                String tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
9082                if (tmResponse == null) {
9083                    /* Then, in case of failure, issue may be due to sim type, retry as a simple sim
9084                     * appType = 1 => SIM
9085                     */
9086                    appType = 1;
9087                    tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
9088                }
9089                logv("Raw Response - " + tmResponse);
9090
9091                if (tmResponse != null && tmResponse.length() > 4) {
9092                    byte[] result = android.util.Base64.decode(tmResponse,
9093                            android.util.Base64.DEFAULT);
9094                    logv("Hex Response -" + makeHex(result));
9095                    int sres_len = result[0];
9096                    String sres = makeHex(result, 1, sres_len);
9097                    int kc_offset = 1+sres_len;
9098                    int kc_len = result[kc_offset];
9099                    String kc = makeHex(result, 1+kc_offset, kc_len);
9100                    sb.append(":" + kc + ":" + sres);
9101                    logv("kc:" + kc + " sres:" + sres);
9102                } else {
9103                    loge("bad response - " + tmResponse);
9104                }
9105            }
9106
9107            String response = sb.toString();
9108            logv("Supplicant Response -" + response);
9109            mWifiNative.simAuthResponse(requestData.networkId, "GSM-AUTH", response);
9110        } else {
9111            loge("could not get telephony manager");
9112        }
9113    }
9114
9115    void handle3GAuthRequest(SimAuthRequestData requestData) {
9116        StringBuilder sb = new StringBuilder();
9117        byte[] rand = null;
9118        byte[] authn = null;
9119        String res_type = "UMTS-AUTH";
9120
9121        if (targetWificonfiguration == null
9122                || targetWificonfiguration.networkId == requestData.networkId) {
9123            logd("id matches targetWifiConfiguration");
9124        } else {
9125            logd("id does not match targetWifiConfiguration");
9126            return;
9127        }
9128        if (requestData.data.length == 2) {
9129            try {
9130                rand = parseHex(requestData.data[0]);
9131                authn = parseHex(requestData.data[1]);
9132            } catch (NumberFormatException e) {
9133                loge("malformed challenge");
9134            }
9135        } else {
9136               loge("malformed challenge");
9137        }
9138
9139        String tmResponse = "";
9140        if (rand != null && authn != null) {
9141            String base64Challenge = android.util.Base64.encodeToString(
9142                    concatHex(rand,authn), android.util.Base64.NO_WRAP);
9143
9144            TelephonyManager tm = (TelephonyManager)
9145                    mContext.getSystemService(Context.TELEPHONY_SERVICE);
9146            if (tm != null) {
9147                int appType = 2; // 2 => USIM
9148                tmResponse = tm.getIccSimChallengeResponse(appType, base64Challenge);
9149                logv("Raw Response - " + tmResponse);
9150            } else {
9151                loge("could not get telephony manager");
9152            }
9153        }
9154
9155        if (tmResponse != null && tmResponse.length() > 4) {
9156            byte[] result = android.util.Base64.decode(tmResponse,
9157                    android.util.Base64.DEFAULT);
9158            loge("Hex Response - " + makeHex(result));
9159            byte tag = result[0];
9160            if (tag == (byte) 0xdb) {
9161                logv("successful 3G authentication ");
9162                int res_len = result[1];
9163                String res = makeHex(result, 2, res_len);
9164                int ck_len = result[res_len + 2];
9165                String ck = makeHex(result, res_len + 3, ck_len);
9166                int ik_len = result[res_len + ck_len + 3];
9167                String ik = makeHex(result, res_len + ck_len + 4, ik_len);
9168                sb.append(":" + ik + ":" + ck + ":" + res);
9169                logv("ik:" + ik + "ck:" + ck + " res:" + res);
9170            } else if (tag == (byte) 0xdc) {
9171                loge("synchronisation failure");
9172                int auts_len = result[1];
9173                String auts = makeHex(result, 2, auts_len);
9174                res_type = "UMTS-AUTS";
9175                sb.append(":" + auts);
9176                logv("auts:" + auts);
9177            } else {
9178                loge("bad response - unknown tag = " + tag);
9179                return;
9180            }
9181        } else {
9182            loge("bad response - " + tmResponse);
9183            return;
9184        }
9185
9186        String response = sb.toString();
9187        logv("Supplicant Response -" + response);
9188        mWifiNative.simAuthResponse(requestData.networkId, res_type, response);
9189    }
9190}
9191