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