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