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