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