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