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