WifiAutoJoinController.java revision f22d23092ab37286a5ef9d257d5bb32c421d2669
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 android.content.Context;
20
21import android.net.NetworkKey;
22import android.net.NetworkScoreManager;
23import android.net.wifi.WifiConfiguration;
24import android.net.wifi.ScanResult;
25import android.net.wifi.WifiManager;
26import android.net.wifi.WifiParser;
27
28import android.os.SystemClock;
29import android.util.Log;
30
31import java.util.Iterator;
32import java.util.HashMap;
33import java.util.List;
34import java.util.Date;
35
36/**
37 * AutoJoin controller is responsible for WiFi Connect decision
38 *
39 * It runs in the thread context of WifiStateMachine
40 *
41 */
42public class WifiAutoJoinController {
43
44    private Context mContext;
45    private WifiStateMachine mWifiStateMachine;
46    private WifiConfigStore mWifiConfigStore;
47    private WifiTrafficPoller mWifiTrafficPoller;
48    private WifiNative mWifiNative;
49
50    private NetworkScoreManager scoreManager;
51    private WifiNetworkScoreCache mNetworkScoreCache;
52
53
54    private static final String TAG = "WifiAutoJoinController ";
55    private static final boolean DBG = true;
56    private static final boolean VDBG = false;
57    private static final boolean mStaStaSupported = false;
58    private static final int SCAN_RESULT_CACHE_SIZE = 80;
59
60
61    private HashMap<String, ScanResult> scanResultCache =
62            new HashMap<String, ScanResult>();
63
64    WifiAutoJoinController(Context c, WifiStateMachine w, WifiConfigStore s,
65                           WifiTrafficPoller t, WifiNative n) {
66        mContext = c;
67        mWifiStateMachine = w;
68        mWifiConfigStore = s;
69        mWifiTrafficPoller = t;
70        mWifiNative = n;
71        mNetworkScoreCache = null;
72        scoreManager = (NetworkScoreManager) mContext.getSystemService(Context.NETWORK_SCORE_SERVICE);
73        if (scoreManager == null)
74            logDbg("Registered scoreManager NULL " + " service " + Context.NETWORK_SCORE_SERVICE);
75        else
76            logDbg("Registered scoreManager NOT NULL" + " service " + Context.NETWORK_SCORE_SERVICE);
77
78        if (scoreManager != null) {
79            // NETwork SCORE SERVICE doesnt seem to be working, so do nothing for now
80            mNetworkScoreCache = new WifiNetworkScoreCache();
81            scoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
82        } else {
83            logDbg("No network score service: Couldnt register as a WiFi score Manager, type="
84                    + Integer.toString(NetworkKey.TYPE_WIFI)
85                    + " service " + Context.NETWORK_SCORE_SERVICE);
86            mNetworkScoreCache = null;
87        }
88    }
89
90    int mScanResultMaximumAge = 30000; /* milliseconds unit */
91
92    /* flush out scan results older than mScanResultMaximumAge */
93    private void ageScanResultsOut(int delay) {
94        if (delay <= 0) {
95            delay = mScanResultMaximumAge; //something sane
96        }
97        Date now = new Date();
98        long milli = now.getTime();
99        if (VDBG) {
100            logDbg("ageScanResultsOut delay " + Integer.valueOf(delay) + " size "
101                    + Integer.valueOf(scanResultCache.size()) + " now " + Long.valueOf(milli));
102        }
103
104        Iterator<HashMap.Entry<String,ScanResult>> iter = scanResultCache.entrySet().iterator();
105        while (iter.hasNext()) {
106            HashMap.Entry<String,ScanResult> entry = iter.next();
107            ScanResult result = entry.getValue();
108
109            if ((result.seen + delay) < milli) {
110                iter.remove();
111            }
112        }
113    }
114
115    /* Check if this network is known to kepler and return its score */
116    private int isScoredNetwork(ScanResult result) {
117        if (mNetworkScoreCache == null)
118            return 0;
119        return mNetworkScoreCache.getNetworkScore(result);
120    }
121
122
123    void addToScanCache(List<ScanResult> scanList) {
124        WifiConfiguration associatedConfig;
125
126        for(ScanResult result: scanList) {
127            result.seen = System.currentTimeMillis();
128
129            ScanResult sr = scanResultCache.get(result.BSSID);
130            if (sr != null) {
131                // if there was a previous cache result for this BSSID, average the RSSI values
132
133                int previous_rssi = sr.level;
134                long previously_seen_milli = sr.seen;
135
136                /* average RSSI with previously seen instances of this scan result */
137                int avg_rssi = result.level;
138
139                if ((previously_seen_milli > 0)
140                        && (previously_seen_milli < mScanResultMaximumAge/2)) {
141
142                    /*
143                    *
144                    * previously_seen_milli = 0 => RSSI = 0.5 * previous_seen_rssi + 0.5 * new_rssi
145                    *
146                    * If previously_seen_milli is 15+ seconds old:
147                    *      previously_seen_milli = 15000 => RSSI = new_rssi
148                    *
149                    */
150
151                    double alpha = 0.5 - (double)previously_seen_milli
152                            / (double)mScanResultMaximumAge;
153
154                    avg_rssi = (int)((double)avg_rssi * (1-alpha) + (double)previous_rssi * alpha);
155
156                }
157                result.level = avg_rssi;
158
159                //remove the previous Scan Result
160                scanResultCache.remove(result.BSSID);
161            }
162
163            scanResultCache.put(result.BSSID, new ScanResult(result));
164
165            ScanResult srn = scanResultCache.get(result.BSSID);
166
167            //add this BSSID to the scanResultCache of the relevant WifiConfiguration
168            associatedConfig = mWifiConfigStore.updateSavedNetworkHistory(result);
169
170            //try to associate this BSSID to an existing Saved Wificonfiguration
171            if (associatedConfig == null) {
172                associatedConfig = mWifiConfigStore.associateWithConfiguration(result);
173                if (associatedConfig != null) {
174                    if (VDBG) {
175                        logDbg("addToScanCache save associated config "
176                                + associatedConfig.SSID + " with " + associatedConfig.SSID);
177                    }
178                    mWifiStateMachine.sendMessage(WifiManager.SAVE_NETWORK, associatedConfig);
179                }
180            }
181        }
182    }
183
184    void logDbg(String message) {
185        long now = SystemClock.elapsedRealtimeNanos();
186        String ts = String.format("[%,d us] ", now/1000);
187        Log.e(TAG, ts + message   + " stack:"
188                + Thread.currentThread().getStackTrace()[2].getMethodName() +" - "
189                + Thread.currentThread().getStackTrace()[3].getMethodName() +" - "
190                + Thread.currentThread().getStackTrace()[4].getMethodName() +" - "
191                + Thread.currentThread().getStackTrace()[5].getMethodName());
192
193    }
194
195    /* called directly from WifiStateMachine  */
196    void newSupplicantResults() {
197        List<ScanResult> scanList = mWifiStateMachine.syncGetScanResultsList();
198        addToScanCache(scanList);
199        ageScanResultsOut(mScanResultMaximumAge);
200        if (DBG)
201           logDbg("newSupplicantResults size=" + Integer.valueOf(scanResultCache.size()) );
202
203        attemptAutoJoin();
204        mWifiConfigStore.writeKnownNetworkHistory();
205
206    }
207
208
209    /* not used at the moment
210     * should be a call back from WifiScanner HAL ??
211     * this function is not hooked and working yet, it will receive scan results from WifiScanners
212     * with the list of IEs,then populate the capabilities by parsing the IEs and inject the scan
213     * results as normal.
214     */
215    void newHalScanResults() {
216        List<ScanResult> scanList = null;//mWifiScanner.syncGetScanResultsList();
217        String akm = WifiParser.parse_akm(null, null);
218        logDbg(akm);
219        addToScanCache(scanList);
220        ageScanResultsOut(0);
221        attemptAutoJoin();
222        mWifiConfigStore.writeKnownNetworkHistory();
223    }
224
225    /* network link quality changed, called directly from WifiTrafficPoller,
226    or by listening to Link Quality intent */
227    void linkQualitySignificantChange() {
228        attemptAutoJoin();
229    }
230
231    /*
232     * compare a WifiConfiguration against the current network, return a delta score
233     * If not associated, and the candidate will always be better
234     * For instance if the candidate is a home network versus an unknown public wifi,
235     * the delta will be infinite, else compare Kepler scores etc…
236     ***/
237    private int compareNetwork(WifiConfiguration candidate) {
238        WifiConfiguration currentNetwork = mWifiStateMachine.getCurrentWifiConfiguration();
239        if (currentNetwork == null)
240            return 1000;
241
242        if (candidate.configKey(true).equals(currentNetwork.configKey(true))) {
243            return -1;
244        }
245
246        int order = compareWifiConfigurations(currentNetwork, candidate);
247
248        if (order > 0) {
249            //ascending: currentNetwork < candidate
250            return 10; //will try switch over to the candidate
251        }
252
253        return 0;
254    }
255
256
257
258    public void updateSavedConfigurationsPriorities(int netId) {
259
260        WifiConfiguration selected = mWifiConfigStore.getWifiConfiguration(netId);
261        if (selected == null) {
262            return;
263        }
264
265        // reenable autojoin for this network,
266        // since the user want to connect to this configuration
267        selected.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED;
268
269        if (DBG) {
270            if (selected.connectChoices != null) {
271                logDbg("updateSavedConfigurationsPriorities will update "
272                        + Integer.toString(netId) + " now: "
273                        + Integer.toString(selected.connectChoices.size()));
274            } else {
275                logDbg("updateSavedConfigurationsPriorities will update "
276                        + Integer.toString(netId));
277            }
278        }
279
280        List<WifiConfiguration> networks =  mWifiConfigStore.getRecentConfiguredNetworks(12000, true);
281        if (networks == null)
282            return;
283
284        for (WifiConfiguration config: networks) {
285
286            if (DBG)
287                logDbg("updateSavedConfigurationsPriorities got " + config.SSID);
288
289            if (selected.configKey(true).equals(config.configKey(true))) {
290                continue;
291            }
292
293            //we were preferred over a recently seen config
294            if (selected.connectChoices == null) {
295                selected.connectChoices = new HashMap<String, Integer>();
296            }
297
298            int rssi = WifiConfiguration.INVALID_RSSI;
299            if (config.visibility != null) {
300                rssi = config.visibility.rssi5;
301                if (config.visibility.rssi24 > rssi)
302                    rssi = config.visibility.rssi24;
303            }
304            if (rssi < -80) {
305                continue;
306            }
307
308            //remember the user's choice:
309            //add the recently seen config to the selected's choice
310            logDbg("updateSavedConfigurationsPriorities add a choice " + selected.configKey(true)
311                    + " over " + config.configKey(true) + " RSSI " + Integer.toString(rssi));
312            selected.connectChoices.put(config.configKey(true), rssi);
313
314            if (config.connectChoices != null) {
315                if (VDBG)
316                    logDbg("updateSavedConfigurationsPriorities try to remove "
317                            + selected.configKey(true) + " from " + config.configKey(true));
318
319                //remove the selected from the recently seen config's array
320                config.connectChoices.remove(selected.configKey(true));
321            }
322            printChoices(config);
323        }
324
325        if (selected.connectChoices != null) {
326            if (VDBG) logDbg("updateSavedConfigurationsPriorities " + Integer.toString(netId)
327                    + " now: " + Integer.toString(selected.connectChoices.size()));
328        }
329
330        mWifiConfigStore.writeKnownNetworkHistory();
331    }
332
333    void printChoices(WifiConfiguration config) {
334        int num = 0;
335        if (config.connectChoices!= null) {
336            num = config.connectChoices.size();
337        }
338
339        logDbg("printChoices " + config.SSID + " num choices: " + Integer.toString(num));
340        if (config.connectChoices!= null) {
341            for (String key : config.connectChoices.keySet()) {
342                logDbg("                 " + key);
343            }
344        }
345    }
346
347
348    boolean hasConnectChoice(WifiConfiguration source, WifiConfiguration target) {
349        boolean found = false;
350        if (source == null)
351            return false;
352        if (target == null)
353            return false;
354
355        if (source.connectChoices != null) {
356            if ( source.connectChoices.get(target.configKey(true)) != null) {
357                found = true;
358            }
359        }
360
361        if (source.linkedConfigurations != null) {
362            for (String key : source.linkedConfigurations.keySet()) {
363                WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(key);
364                if (config != null) {
365                    if (config.connectChoices != null) {
366                        if (config.connectChoices.get(target.configKey(true)) != null) {
367                            found = true;
368                        }
369                    }
370                }
371            }
372        }
373        return found;
374    }
375
376    int compareWifiConfigurationsRSSI(WifiConfiguration a, WifiConfiguration b) {
377        int order = 0;
378        int boost5 = 25;
379
380        WifiConfiguration.Visibility astatus = a.visibility;
381        WifiConfiguration.Visibility bstatus = b.visibility;
382        if (astatus == null || bstatus == null) {
383            //error
384            logDbg("compareWifiConfigurations NULL band status!");
385            return 0;
386        }
387        if ((astatus.rssi5 > -70) && (bstatus.rssi5 == -127)
388                && ((astatus.rssi5+boost5) > (bstatus.rssi24))) {
389            //a is seen on 5GHz with good RSSI, greater rssi than b
390            //a is of higher priority - descending
391            order = -1;
392        } else if ((bstatus.rssi5 > -70) && (astatus.rssi5 == -127)
393                && ((bstatus.rssi5+boost5) > (bstatus.rssi24))) {
394            //b is seen on 5GHz with good RSSI, greater rssi than a
395            //a is of lower priority - ascending
396            order = 1;
397        }
398        return order;
399    }
400
401    int compareWifiConfigurations(WifiConfiguration a, WifiConfiguration b) {
402        int order = 0;
403
404        boolean linked = false;
405
406        if ((a.linkedConfigurations != null) && (b.linkedConfigurations != null)) {
407            if ((a.linkedConfigurations.get(b.configKey(true))!= null)
408                    && (b.linkedConfigurations.get(a.configKey(true))!= null)) {
409                linked = true;
410            }
411        }
412
413        if (a.ephemeral && b.ephemeral == false) {
414            if (VDBG) {
415                logDbg("compareWifiConfigurations ephemeral and prefers " + b.SSID
416                        + " over " + a.SSID);
417            }
418            return 1; //b is of higher priority - ascending
419        }
420        if (b.ephemeral && a.ephemeral == false) {
421            if (VDBG) {
422                logDbg("compareWifiConfigurations ephemeral and prefers " +a.SSID
423                        + " over " + b.SSID);
424            }
425            return -1; //a is of higher priority - descending
426        }
427
428        int boost5 = 25;
429        if (linked) {
430            // then we try prefer 5GHz, and try to ignore user's choice
431            WifiConfiguration.Visibility astatus = a.visibility;
432            WifiConfiguration.Visibility bstatus = b.visibility;
433            if (astatus == null || bstatus == null) {
434                //error
435                logDbg("compareWifiConfigurations NULL band status!");
436                return 0;
437            }
438
439            if (VDBG)  {
440                logDbg("compareWifiConfigurations linked: " + Integer.toString(astatus.rssi5)
441                        + "," + Integer.toString(astatus.rssi24) + "   "
442                        + Integer.toString(bstatus.rssi5) + ","
443                        + Integer.toString(bstatus.rssi24));
444            }
445
446            if ((astatus.rssi5 > -70) && (bstatus.rssi5 == -127)
447                    && ((astatus.rssi5+boost5) > (bstatus.rssi24))) {
448                    //a is seen on 5GHz with good RSSI, greater rssi than b
449                    //a is of higher priority - descending
450                    order = -10;
451
452                if (VDBG) {
453                    logDbg("compareWifiConfigurations linked and prefers " + a.SSID
454                            + " over " + b.SSID
455                            + " due to 5GHz RSSI " + Integer.toString(astatus.rssi5)
456                            + " over: 5=" + Integer.toString(bstatus.rssi5)
457                            + ", 2.4=" + Integer.toString(bstatus.rssi5));
458                }
459            } else if ((bstatus.rssi5 > -70) && (astatus.rssi5 == -127)
460                    && ((bstatus.rssi5+boost5) > (bstatus.rssi24))) {
461                    //b is seen on 5GHz with good RSSI, greater rssi than a
462                    //a is of lower priority - ascending
463                if (VDBG)   {
464                    logDbg("compareWifiConfigurations linked and prefers " + b.SSID
465                            + " over " + a.SSID + " due to 5GHz RSSI "
466                            + Integer.toString(astatus.rssi5) + " over: 5="
467                            + Integer.toString(bstatus.rssi5) + ", 2.4="
468                            + Integer.toString(bstatus.rssi5));
469                }
470                order = 10;
471            }
472        }
473        //assuming that the WifiConfiguration aren't part of the same "extended roam domain",
474        //then compare by user's choice.
475        if (hasConnectChoice(a, b)) {
476            //a is of higher priority - descending
477            order = order -2;
478            if (VDBG)   {
479                logDbg("compareWifiConfigurations prefers -2 " + a.SSID
480                        + " over " + b.SSID + " due to user choice order -> " + Integer.toString(order));
481            }
482        }
483
484        if (hasConnectChoice(b, a)) {
485            //a is of lower priority - ascending
486            order = order + 2;
487            if (VDBG)   {
488                logDbg("compareWifiConfigurations prefers +2 " + b.SSID + " over "
489                        + a.SSID + " due to user choice order ->" + Integer.toString(order));
490            }
491        }
492
493        if (order == 0) {
494            //we don't know anything - pick the last seen i.e. K behavior
495            //we should do this only for recently picked configurations
496            if (a.priority > b.priority) {
497                //a is of higher priority - descending
498                if (VDBG)   {
499                    logDbg("compareWifiConfigurations prefers -1 " + a.SSID + " over "
500                            + b.SSID + " due to priority");
501                }
502
503                order = -1;
504            } else if (a.priority < b.priority) {
505                //a is of lower priority - ascending
506                if (VDBG)  {
507                    logDbg("compareWifiConfigurations prefers +1 " + b.SSID + " over "
508                            + a.SSID + " due to priority");
509                }
510
511              order = 1;
512            } else {
513                //maybe just look at RSSI or band
514                if (VDBG)  {
515                    logDbg("compareWifiConfigurations prefers +1 " + b.SSID + " over "
516                            + a.SSID + " due to nothing");
517                }
518
519                order = compareWifiConfigurationsRSSI(a, b); //compare RSSI
520            }
521        }
522
523        String sorder = " == ";
524        if (order > 0)
525            sorder = " < ";
526        if (order < 0)
527            sorder = " > ";
528
529        if (VDBG)   {
530            logDbg("compareWifiConfigurations Done: " + a.SSID + sorder
531                    + b.SSID + " order " + Integer.toString(order));
532        }
533
534        return order;
535    }
536
537    /* attemptAutoJoin function implement the core of the a network switching algorithm */
538    void attemptAutoJoin() {
539        WifiConfiguration candidate = null;
540
541        /* obtain the subset of recently seen networks */
542        List<WifiConfiguration> list = mWifiConfigStore.getRecentConfiguredNetworks(3000, true);
543        if (list == null) {
544            if (VDBG)  logDbg("attemptAutoJoin nothing");
545            return;
546        }
547
548        /* find the currently connected network: ask the supplicant directly */
549        String val = mWifiNative.status();
550        String status[] = val.split("\\r?\\n");
551        if (VDBG) {
552            logDbg("attemptAutoJoin() status=" + val + " split="
553                    + Integer.toString(status.length));
554        }
555
556        int currentNetId = -1;
557        for (String key : status) {
558            if (key.regionMatches(0, "id=", 0, 3)) {
559                int idx = 3;
560                currentNetId = 0;
561                while (idx < key.length()) {
562                    char c = key.charAt(idx);
563
564                    if ((c >= 0x30) && (c <= 0x39)) {
565                        currentNetId *= 10;
566                        currentNetId += c - 0x30;
567                        idx++;
568                    } else {
569                        break;
570                    }
571                }
572            }
573        }
574        logDbg("attemptAutoJoin() num recent config " + Integer.toString(list.size())
575                +  " ---> currentId=" + Integer.toString(currentNetId));
576
577        /* select Best Network candidate from known WifiConfigurations */
578        for (WifiConfiguration config : list) {
579            if ((config.status == WifiConfiguration.Status.DISABLED)
580                    && (config.disableReason == WifiConfiguration.DISABLED_AUTH_FAILURE)) {
581                logDbg("attemptAutoJoin skip candidate due to auth failure "
582                        + config.SSID + " key " + config.configKey(true));
583                continue;
584            }
585            if (config.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
586                logDbg("attemptAutoJoin skip candidate due to auto join status "
587                        + Integer.toString(config.autoJoinStatus) + " " + config.SSID + " key "
588                        + config.configKey(true));
589                continue;
590            }
591
592            if (config.networkId == currentNetId) {
593                logDbg("attemptAutoJoin skip current candidate  " + Integer.toString(currentNetId)
594                        + " key " + config.configKey(true));
595                continue;
596            }
597
598            if (DBG) logDbg("attemptAutoJoin trying candidate id=" + config.networkId + " "
599                    + config.SSID + " key " + config.configKey(true));
600
601            if (candidate == null) {
602                candidate = config;
603            } else {
604                if (VDBG)  {
605                    logDbg("attemptAutoJoin will compare candidate  " + candidate.SSID
606                            + " with " + config.SSID + " key " + config.configKey(true));
607                }
608
609                int order = compareWifiConfigurations(candidate, config);
610
611                if (VDBG) {
612                    logDbg("attemptAutoJoin did compare candidate " + Integer.toString(order));
613                }
614
615                if (order > 0) {
616                    //ascending : candidate < config
617                    candidate = config;
618                }
619            }
620        }
621
622        /* now, go thru scan result to try finding a better Herrevad network */
623        if (mNetworkScoreCache != null) {
624            int rssi5 = WifiConfiguration.INVALID_RSSI;
625            int rssi24 = WifiConfiguration.INVALID_RSSI;
626            WifiConfiguration.Visibility visibility;
627            if (candidate != null) {
628                rssi5 = candidate.visibility.rssi5;
629                rssi24 = candidate.visibility.rssi24;
630            }
631
632            //get current date
633            Date now = new Date();
634            long now_ms = now.getTime();
635
636            if (rssi5 < -60 && rssi24 < -70) {
637                for (ScanResult result : scanResultCache.values()) {
638                    if ((now_ms - result.seen) < 3000) {
639                        int score = mNetworkScoreCache.getNetworkScore(result);
640                        if (score > 0) {
641                            // try any arbitrary formula for now, adding apple and oranges,
642                            // i.e. adding network score and "dBm over noise"
643                           if (result.frequency < 4000) {
644                                if ((result.level + score) > (rssi24 -40)) {
645                                    // force it as open, TBD should we otherwise verify that this
646                                    // BSSID only supports open??
647                                    result.capabilities = "";
648
649                                    //switch to this scan result
650                                    candidate =
651                                            mWifiConfigStore.wifiConfigurationFromScanResult(result);
652                                    candidate.ephemeral = true;
653                                }
654                           } else {
655                                if ((result.level + score) > (rssi5 -30)) {
656                                    // force it as open, TBD should we otherwise verify that this
657                                    // BSSID only supports open??
658                                    result.capabilities = "";
659
660                                    //switch to this scan result
661                                    candidate =
662                                            mWifiConfigStore.wifiConfigurationFromScanResult(result);
663                                    candidate.ephemeral = true;
664                                }
665                           }
666                        }
667                    }
668                }
669            }
670        }
671
672        if (candidate != null) {
673	    /* if candidate is found, check the state of the connection so as
674	       to decide if we should be acting on this candidate and switching over */
675           if (VDBG) {
676               logDbg("attemptAutoJoin did find candidate " + candidate.SSID
677                       + " key " + candidate.configKey(true));
678           }
679
680            int networkDelta = compareNetwork(candidate);
681            if (networkDelta > 0)
682                logDbg("attemptAutoJoin did find candidate " + candidate.SSID
683                        + " for delta " + Integer.toString(networkDelta));
684
685            /* ASK traffic poller permission to switch:
686                for instance,
687                if user is currently streaming voice traffic,
688                then don’t switch regardless of the delta */
689
690            if (mWifiTrafficPoller.shouldSwitchNetwork(networkDelta)) {
691                if (mStaStaSupported) {
692
693                } else {
694                    if (DBG) {
695                        logDbg("AutoJoin auto connect to netId "
696                                + Integer.toString(candidate.networkId)
697                                + " SSID " + candidate.SSID);
698                    }
699
700                    mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_CONNECT,
701                            candidate.networkId);
702                    //mWifiConfigStore.enableNetworkWithoutBroadcast(candidate.networkId, true);
703
704                    //we would do the below only if we want to persist the new choice
705                    //mWifiConfigStore.selectNetwork(candidate.networkId);
706
707                }
708            }
709        }
710        if (VDBG) logDbg("Done attemptAutoJoin");
711    }
712}
713
714