WifiAutoJoinController.java revision c290d8dff6172d5fde7b9dfd74d3a20785dab246
1f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle/*
262f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle * Copyright (C) 2014 The Android Open Source Project
3f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle *
4f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle * Licensed under the Apache License, Version 2.0 (the "License");
5f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle * you may not use this file except in compliance with the License.
6f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle * You may obtain a copy of the License at
7f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle *
8f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle *      http://www.apache.org/licenses/LICENSE-2.0
9f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle *
10f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle * Unless required by applicable law or agreed to in writing, software
11f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle * distributed under the License is distributed on an "AS IS" BASIS,
12f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle * See the License for the specific language governing permissions and
14f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle * limitations under the License.
15f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle */
16f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
17f22d23092ab37286a5ef9d257d5bb32c421d2669vandwallepackage com.android.server.wifi;
18f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
19f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalleimport android.content.Context;
20f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
21f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalleimport android.net.NetworkKey;
22f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalleimport android.net.NetworkScoreManager;
230c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalleimport android.net.WifiKey;
24c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalleimport android.net.wifi.*;
25f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
26f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalleimport android.os.SystemClock;
27b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalleimport android.os.Process;
28c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalleimport android.text.TextUtils;
29f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalleimport android.util.Log;
30f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
310c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalleimport java.util.ArrayList;
32f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalleimport java.util.Iterator;
33f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalleimport java.util.HashMap;
34f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalleimport java.util.List;
35f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
36f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle/**
37f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle * AutoJoin controller is responsible for WiFi Connect decision
38f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle *
39f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle * It runs in the thread context of WifiStateMachine
40f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle *
41f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle */
42f22d23092ab37286a5ef9d257d5bb32c421d2669vandwallepublic class WifiAutoJoinController {
43f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
44f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private Context mContext;
45f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private WifiStateMachine mWifiStateMachine;
46f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private WifiConfigStore mWifiConfigStore;
47f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private WifiNative mWifiNative;
48f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
49f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private NetworkScoreManager scoreManager;
50f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private WifiNetworkScoreCache mNetworkScoreCache;
51f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
52f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private static final String TAG = "WifiAutoJoinController ";
53ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle    private static boolean DBG = false;
54ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle    private static boolean VDBG = false;
55f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private static final boolean mStaStaSupported = false;
56f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private static final int SCAN_RESULT_CACHE_SIZE = 80;
57f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
58c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle    public static int mScanResultMaximumAge = 40000; /* milliseconds unit */
59c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle
60453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle    private String mCurrentConfigurationKey = null; //used by autojoin
61f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
62f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private HashMap<String, ScanResult> scanResultCache =
63f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            new HashMap<String, ScanResult>();
64f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
65c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle    private WifiConnectionStatistics mWifiConnectionStatistics;
66c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle
67c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle    /* for debug purpose only : the untrusted SSID we would be connected to if we had VPN */
68c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle    String lastUntrustedBSSID = null;
69c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle
70c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle    /* For debug purpose only: if the scored override a score */
71c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle    boolean didOverride = false;
72c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle
73931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle    // Lose the non-auth failure blacklisting after 8 hours
744dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle    private final static long loseBlackListHardMilli = 1000 * 60 * 60 * 8;
75931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle    // Lose some temporary blacklisting after 30 minutes
764dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle    private final static long loseBlackListSoftMilli = 1000 * 60 * 30;
7727355a942653264388e909a4276196ee63e57811vandwalle
78b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle    public static final int AUTO_JOIN_IDLE = 0;
79b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle    public static final int AUTO_JOIN_ROAMING = 1;
80b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle    public static final int AUTO_JOIN_EXTENDED_ROAMING = 2;
81b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle    public static final int AUTO_JOIN_OUT_OF_NETWORK_ROAMING = 3;
82b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle
83f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    WifiAutoJoinController(Context c, WifiStateMachine w, WifiConfigStore s,
84c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                           WifiConnectionStatistics st, WifiNative n) {
85f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        mContext = c;
86f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        mWifiStateMachine = w;
87f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        mWifiConfigStore = s;
88f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        mWifiNative = n;
89f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        mNetworkScoreCache = null;
90c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle        mWifiConnectionStatistics = st;
9121bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle        scoreManager =
9221bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                (NetworkScoreManager) mContext.getSystemService(Context.NETWORK_SCORE_SERVICE);
93f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (scoreManager == null)
94f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            logDbg("Registered scoreManager NULL " + " service " + Context.NETWORK_SCORE_SERVICE);
95f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
96f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (scoreManager != null) {
97f13817203179f41620514718c8668ae7e418f8afJeff Davidson            mNetworkScoreCache = new WifiNetworkScoreCache(mContext);
98f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            scoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache);
99f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        } else {
100f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            logDbg("No network score service: Couldnt register as a WiFi score Manager, type="
101f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    + Integer.toString(NetworkKey.TYPE_WIFI)
102f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    + " service " + Context.NETWORK_SCORE_SERVICE);
103f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            mNetworkScoreCache = null;
104f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
105f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
106f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
107ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle    void enableVerboseLogging(int verbose) {
108ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle        if (verbose > 0 ) {
109abde872adced15dfb6781fb71959453d963326dbYuhao Zheng            DBG = true;
110ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle            VDBG = true;
111ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle        } else {
112abde872adced15dfb6781fb71959453d963326dbYuhao Zheng            DBG = false;
113ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle            VDBG = false;
114ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle        }
115ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle    }
116ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle
117931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle    /**
118931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle     * Flush out scan results older than mScanResultMaximumAge
119ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle     *
120931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle     */
121f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private void ageScanResultsOut(int delay) {
122f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (delay <= 0) {
123931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            delay = mScanResultMaximumAge; // Something sane
124f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
125b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        long milli = System.currentTimeMillis();
126f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (VDBG) {
127f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            logDbg("ageScanResultsOut delay " + Integer.valueOf(delay) + " size "
128f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    + Integer.valueOf(scanResultCache.size()) + " now " + Long.valueOf(milli));
129f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
130f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
131f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        Iterator<HashMap.Entry<String,ScanResult>> iter = scanResultCache.entrySet().iterator();
132f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        while (iter.hasNext()) {
133f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            HashMap.Entry<String,ScanResult> entry = iter.next();
134f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            ScanResult result = entry.getValue();
135f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
136f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if ((result.seen + delay) < milli) {
137f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                iter.remove();
138f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
139f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
140f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
141f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
142f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    void addToScanCache(List<ScanResult> scanList) {
143f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        WifiConfiguration associatedConfig;
144f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
1450c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle        ArrayList<NetworkKey> unknownScanResults = new ArrayList<NetworkKey>();
1460c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle
147c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle        long nowMs = System.currentTimeMillis();
148f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        for(ScanResult result: scanList) {
1491fcf3c6d2b9ed65573e1e7c55fc5a30ebd364c4fYuhao Zheng            if (result.SSID == null) continue;
150c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle
151c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            // Make sure we record the last time we saw this result
152f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            result.seen = System.currentTimeMillis();
153f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
154c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            // Fetch the previous instance for this result
155f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            ScanResult sr = scanResultCache.get(result.BSSID);
156f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (sr != null) {
157931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // If there was a previous cache result for this BSSID, average the RSSI values
158c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                result.averageRssi(sr.level, sr.seen, mScanResultMaximumAge);
159f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
160c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                // Remove the previous Scan Result - this is not necessary
161f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                scanResultCache.remove(result.BSSID);
1620c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle            } else {
1630c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle                if (!mNetworkScoreCache.isScoredNetwork(result)) {
16421bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                    WifiKey wkey;
165931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                    // Quoted SSIDs are the only one valid at this stage
16621bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                    try {
16721bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                        wkey = new WifiKey("\"" + result.SSID + "\"", result.BSSID);
16821bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                    } catch (IllegalArgumentException e) {
16921bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                        logDbg("AutoJoinController: received badly encoded SSID=[" + result.SSID +
17021bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                                "] ->skipping this network");
17121bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                        wkey = null;
17221bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                    }
17321bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                    if (wkey != null) {
17421bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                        NetworkKey nkey = new NetworkKey(wkey);
1752451dbcc4f9641df188326215b204b798eb70c46vandwalle                        //if we don't know this scan result then request a score from the scorer
17621bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                        unknownScanResults.add(nkey);
17721bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                    }
1780c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle                }
179f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
180f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
181f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            scanResultCache.put(result.BSSID, new ScanResult(result));
182f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
183931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // Add this BSSID to the scanResultCache of the relevant WifiConfiguration
184f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            associatedConfig = mWifiConfigStore.updateSavedNetworkHistory(result);
185f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
186931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // Try to associate this BSSID to an existing Saved WifiConfiguration
187f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (associatedConfig == null) {
188c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                // We couldn't associate the scan result to a saved configuration
189c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                // Hence it is untrusted
190c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                result.untrusted = true;
191f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                associatedConfig = mWifiConfigStore.associateWithConfiguration(result);
1921fcf3c6d2b9ed65573e1e7c55fc5a30ebd364c4fYuhao Zheng                if (associatedConfig != null && associatedConfig.SSID != null) {
193f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    if (VDBG) {
194f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                        logDbg("addToScanCache save associated config "
195f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                                + associatedConfig.SSID + " with " + associatedConfig.SSID);
196f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    }
197f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    mWifiStateMachine.sendMessage(WifiManager.SAVE_NETWORK, associatedConfig);
198f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                }
199f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
200f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
2010c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle
2020c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle        if (unknownScanResults.size() != 0) {
2030c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle            NetworkKey[] newKeys =
2040c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle                    unknownScanResults.toArray(new NetworkKey[unknownScanResults.size()]);
205931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // Kick the score manager, we will get updated scores asynchronously
2060c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle            scoreManager.requestScores(newKeys);
2070c8b99a3b78e458a5617cc449e2efe69c5bdd531vandwalle        }
208f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
209f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
210f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    void logDbg(String message) {
2110888ce6f90bdaeee799dd8361ea4781e23a33b87vandwalle        logDbg(message, false);
212ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle    }
213ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle
214ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle    void logDbg(String message, boolean stackTrace) {
215f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        long now = SystemClock.elapsedRealtimeNanos();
2160888ce6f90bdaeee799dd8361ea4781e23a33b87vandwalle        String ts = String.format("[%,d us] ", now / 1000);
217ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle        if (stackTrace) {
218ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle            Log.e(TAG, ts + message + " stack:"
219ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                    + Thread.currentThread().getStackTrace()[2].getMethodName() + " - "
220ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                    + Thread.currentThread().getStackTrace()[3].getMethodName() + " - "
221ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                    + Thread.currentThread().getStackTrace()[4].getMethodName() + " - "
222ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                    + Thread.currentThread().getStackTrace()[5].getMethodName());
223ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle        } else {
224ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle            Log.e(TAG, ts + message);
225ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle        }
226f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
227f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
228931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle    // Called directly from WifiStateMachine
229f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    void newSupplicantResults() {
230f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        List<ScanResult> scanList = mWifiStateMachine.syncGetScanResultsList();
231f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        addToScanCache(scanList);
232f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        ageScanResultsOut(mScanResultMaximumAge);
233f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (DBG)
234f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle           logDbg("newSupplicantResults size=" + Integer.valueOf(scanResultCache.size()) );
235f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
236f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        attemptAutoJoin();
237f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        mWifiConfigStore.writeKnownNetworkHistory();
238f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
239f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
240f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
241931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle    /**
242931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle     * Not used at the moment
243f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle     * should be a call back from WifiScanner HAL ??
244f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle     * this function is not hooked and working yet, it will receive scan results from WifiScanners
245f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle     * with the list of IEs,then populate the capabilities by parsing the IEs and inject the scan
246f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle     * results as normal.
247f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle     */
248f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    void newHalScanResults() {
249f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        List<ScanResult> scanList = null;//mWifiScanner.syncGetScanResultsList();
250f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        String akm = WifiParser.parse_akm(null, null);
251f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        logDbg(akm);
252f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        addToScanCache(scanList);
253f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        ageScanResultsOut(0);
254f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        attemptAutoJoin();
255f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        mWifiConfigStore.writeKnownNetworkHistory();
256f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
257f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
258931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle    /**
259931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle     *  network link quality changed, called directly from WifiTrafficPoller,
260931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle     * or by listening to Link Quality intent
261931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle     */
262f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    void linkQualitySignificantChange() {
263f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        attemptAutoJoin();
264f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
265f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
266931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle    /**
267f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle     * compare a WifiConfiguration against the current network, return a delta score
268f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle     * If not associated, and the candidate will always be better
269f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle     * For instance if the candidate is a home network versus an unknown public wifi,
270f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle     * the delta will be infinite, else compare Kepler scores etc…
271b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle     * Negatve return values from this functions are meaningless per se, just trying to
272b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle     * keep them distinct for debug purpose (i.e. -1, -2 etc...)
273931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle     */
274f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    private int compareNetwork(WifiConfiguration candidate) {
275b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        if (candidate == null)
276b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle            return -3;
277b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle
278f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        WifiConfiguration currentNetwork = mWifiStateMachine.getCurrentWifiConfiguration();
279b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle        if (currentNetwork == null) {
280c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle            // Return any absurdly high score, if we are not connected there is no current
281c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle            // network to...
282b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle           return 1000;
283b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle        }
284f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
285f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (candidate.configKey(true).equals(currentNetwork.configKey(true))) {
286b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle            return -2;
287f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
288f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
289c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle        int order = compareWifiConfigurationsTop(currentNetwork, candidate);
290ede507649471f1113e9e1919812115ca5a6bc0c8vandwalle        return order;
291f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
292f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
293ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle    /**
294ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle     * update the network history fields fo that configuration
295ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle     * - if userTriggered, we mark the configuration as "non selfAdded" since the user has seen it
296ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle     * and took over management
297ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle     * - if it is a "connect", remember which network were there at the point of the connect, so
298ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle     * as those networks get a relative lower score than the selected configuration
29962f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle     *
300ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle     * @param netId
301ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle     * @param userTriggered : if the update come from WiFiManager
302ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle     * @param connect : if the update includes a connect
303931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle     */
30462f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle    public void updateConfigurationHistory(int netId, boolean userTriggered, boolean connect) {
305f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        WifiConfiguration selected = mWifiConfigStore.getWifiConfiguration(netId);
306f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (selected == null) {
307c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            logDbg("updateConfigurationHistory nid=" + netId + " no selected configuration!");
308f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            return;
309f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
310f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
311e86c962bb99a8b126ed64ddcc6b112161549e26dvandwalle        if (selected.SSID == null) {
312c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            logDbg("updateConfigurationHistory nid=" + netId +
313c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    " no SSID in selected configuration!");
314e86c962bb99a8b126ed64ddcc6b112161549e26dvandwalle            return;
315e86c962bb99a8b126ed64ddcc6b112161549e26dvandwalle        }
316e86c962bb99a8b126ed64ddcc6b112161549e26dvandwalle
31762f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle        if (userTriggered) {
318931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // Reenable autojoin for this network,
31962f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle            // since the user want to connect to this configuration
32027355a942653264388e909a4276196ee63e57811vandwalle            selected.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
32162f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle            selected.selfAdded = false;
32262f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle        }
323f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
324992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle        if (DBG && userTriggered) {
325f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (selected.connectChoices != null) {
326ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                logDbg("updateConfigurationHistory will update "
327f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                        + Integer.toString(netId) + " now: "
328992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle                        + Integer.toString(selected.connectChoices.size())
329992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle                        + " uid=" + Integer.toString(selected.creatorUid), true);
330f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            } else {
331ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                logDbg("updateConfigurationHistory will update "
332992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle                        + Integer.toString(netId)
333992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle                        + " uid=" + Integer.toString(selected.creatorUid), true);
334f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
335f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
336f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
337ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle        if (connect && userTriggered) {
338ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle            boolean found = false;
3392451dbcc4f9641df188326215b204b798eb70c46vandwalle            int choice = 0;
340c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            int size = 0;
34162f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle            List<WifiConfiguration> networks =
34262f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                    mWifiConfigStore.getRecentConfiguredNetworks(12000, false);
343c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            if (networks != null) size = networks.size();
344c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            logDbg("updateConfigurationHistory found " + size + " networks");
34562f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle            if (networks != null) {
34662f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                for (WifiConfiguration config : networks) {
347992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle                    if (DBG) {
348992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle                        logDbg("updateConfigurationHistory got " + config.SSID + " nid="
349992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle                                + Integer.toString(config.networkId));
350992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle                    }
351f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
35262f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                    if (selected.configKey(true).equals(config.configKey(true))) {
353ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                        found = true;
35462f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                        continue;
35562f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                    }
356f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
3572451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // Compare RSSI values so as to evaluate the strength of the user preference
3582451dbcc4f9641df188326215b204b798eb70c46vandwalle                    int order = compareWifiConfigurationsRSSI(config, selected, null);
3592451dbcc4f9641df188326215b204b798eb70c46vandwalle
3602451dbcc4f9641df188326215b204b798eb70c46vandwalle                    if (order < -30) {
3612451dbcc4f9641df188326215b204b798eb70c46vandwalle                        // Selected configuration is worse than the visible configuration
3622451dbcc4f9641df188326215b204b798eb70c46vandwalle                        // hence register a strong choice so as autojoin cannot override this
3632451dbcc4f9641df188326215b204b798eb70c46vandwalle                        // for instance, the user has select a network
3642451dbcc4f9641df188326215b204b798eb70c46vandwalle                        // with 1 bar over a network with 3 bars...
3652451dbcc4f9641df188326215b204b798eb70c46vandwalle                        choice = 60;
3662451dbcc4f9641df188326215b204b798eb70c46vandwalle                    } else if (order < -20) {
3672451dbcc4f9641df188326215b204b798eb70c46vandwalle                        choice = 50;
3682451dbcc4f9641df188326215b204b798eb70c46vandwalle                    } else if (order < -10) {
3692451dbcc4f9641df188326215b204b798eb70c46vandwalle                        choice = 40;
3702451dbcc4f9641df188326215b204b798eb70c46vandwalle                    } else if (order < 10) {
3712451dbcc4f9641df188326215b204b798eb70c46vandwalle                        // Selected configuration is about same or has a slightly better RSSI
3722451dbcc4f9641df188326215b204b798eb70c46vandwalle                        // hence register a weaker choice, here a difference of at least +/-30 in
3732451dbcc4f9641df188326215b204b798eb70c46vandwalle                        // RSSI comparison triggered by autoJoin will override the choice
3742451dbcc4f9641df188326215b204b798eb70c46vandwalle                        choice = 30;
3752451dbcc4f9641df188326215b204b798eb70c46vandwalle                    } else if (order <= 30) {
3762451dbcc4f9641df188326215b204b798eb70c46vandwalle                        // Selected configuration is better than the visible configuration
3772451dbcc4f9641df188326215b204b798eb70c46vandwalle                        // hence we do not know if the user prefers this configuration strongly
3782451dbcc4f9641df188326215b204b798eb70c46vandwalle                        choice = 20;
3792451dbcc4f9641df188326215b204b798eb70c46vandwalle                    } else {
3802451dbcc4f9641df188326215b204b798eb70c46vandwalle                        choice = 10;
38162f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                    }
382f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
383931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                    // The selected configuration was preferred over a recently seen config
384931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                    // hence remember the user's choice:
385931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                    // add the recently seen config to the selected's connectChoices array
386ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle
387ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                    if (selected.connectChoices == null) {
388ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                        selected.connectChoices = new HashMap<String, Integer>();
389ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                    }
390ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle
391ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                    logDbg("updateConfigurationHistory add a choice " + selected.configKey(true)
3920888ce6f90bdaeee799dd8361ea4781e23a33b87vandwalle                            + " over " + config.configKey(true)
3932451dbcc4f9641df188326215b204b798eb70c46vandwalle                            + " choice " + Integer.toString(choice));
394cf5b8eb8a08c45bd4a82f1f4bb789c8a1b08744fvandwalle
3952451dbcc4f9641df188326215b204b798eb70c46vandwalle                    Integer currentChoice = selected.connectChoices.get(config.configKey(true));
3962451dbcc4f9641df188326215b204b798eb70c46vandwalle                    if (currentChoice == null || currentChoice.intValue() < choice) {
3972451dbcc4f9641df188326215b204b798eb70c46vandwalle                        // Add the visible config to the selected's connect choice list
3982451dbcc4f9641df188326215b204b798eb70c46vandwalle                        selected.connectChoices.put(config.configKey(true), choice);
3992451dbcc4f9641df188326215b204b798eb70c46vandwalle                    }
400f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
40162f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                    if (config.connectChoices != null) {
402ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                        if (VDBG) {
403ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                            logDbg("updateConfigurationHistory will remove "
40462f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                                    + selected.configKey(true) + " from " + config.configKey(true));
405ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                        }
406931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                        // Remove the selected from the recently seen config's connectChoice list
40762f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                        config.connectChoices.remove(selected.configKey(true));
4080888ce6f90bdaeee799dd8361ea4781e23a33b87vandwalle
4090888ce6f90bdaeee799dd8361ea4781e23a33b87vandwalle                        if (selected.linkedConfigurations != null) {
410931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                           // Remove the selected's linked configuration from the
411931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                           // recently seen config's connectChoice list
4120888ce6f90bdaeee799dd8361ea4781e23a33b87vandwalle                           for (String key : selected.linkedConfigurations.keySet()) {
4130888ce6f90bdaeee799dd8361ea4781e23a33b87vandwalle                               config.connectChoices.remove(key);
4140888ce6f90bdaeee799dd8361ea4781e23a33b87vandwalle                           }
4150888ce6f90bdaeee799dd8361ea4781e23a33b87vandwalle                        }
41662f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                    }
417ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                }
418ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                if (found == false) {
4192451dbcc4f9641df188326215b204b798eb70c46vandwalle                     // We haven't found the configuration that the user just selected in our
4202451dbcc4f9641df188326215b204b798eb70c46vandwalle                     // scan cache.
4212451dbcc4f9641df188326215b204b798eb70c46vandwalle                     // In that case we will need a new scan before attempting to connect to this
4222451dbcc4f9641df188326215b204b798eb70c46vandwalle                     // configuration anyhow and thus we can process the scan results then.
423ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                     logDbg("updateConfigurationHistory try to connect to an old network!! : "
424ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                             + selected.configKey());
42562f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                }
426f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
42762f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                if (selected.connectChoices != null) {
42862f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                    if (VDBG)
429ecd2b88214b5d214fd1f63a9560caff9058912ddvandwalle                        logDbg("updateConfigurationHistory " + Integer.toString(netId)
43062f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                                + " now: " + Integer.toString(selected.connectChoices.size()));
43162f1d0ca8ea4466628f6ff179b1f20e1279fa7e0vandwalle                }
432f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
433f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
434992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle
435931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        // TODO: write only if something changed
436992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle        if (userTriggered || connect) {
437992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle            mWifiConfigStore.writeKnownNetworkHistory();
438992ae00f25a9cc22cf5db3261bd7e72927069cf7vandwalle        }
439f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
440f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
4412451dbcc4f9641df188326215b204b798eb70c46vandwalle    int getConnectChoice(WifiConfiguration source, WifiConfiguration target) {
4422451dbcc4f9641df188326215b204b798eb70c46vandwalle        Integer choice = null;
4432451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (source == null || target == null) {
4442451dbcc4f9641df188326215b204b798eb70c46vandwalle            return 0;
445f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
446f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
4472451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (source.connectChoices != null
4482451dbcc4f9641df188326215b204b798eb70c46vandwalle                && source.connectChoices.containsKey(target.configKey(true))) {
4492451dbcc4f9641df188326215b204b798eb70c46vandwalle            choice = source.connectChoices.get(target.configKey(true));
4502451dbcc4f9641df188326215b204b798eb70c46vandwalle        } else if (source.linkedConfigurations != null) {
451f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            for (String key : source.linkedConfigurations.keySet()) {
452f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(key);
453f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                if (config != null) {
454f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    if (config.connectChoices != null) {
4552451dbcc4f9641df188326215b204b798eb70c46vandwalle                        choice = config.connectChoices.get(target.configKey(true));
456f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    }
457f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                }
458f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
4592451dbcc4f9641df188326215b204b798eb70c46vandwalle        }
4602451dbcc4f9641df188326215b204b798eb70c46vandwalle
4612451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (choice == null) {
4622451dbcc4f9641df188326215b204b798eb70c46vandwalle            //We didn't find the connect choice
4632451dbcc4f9641df188326215b204b798eb70c46vandwalle            return 0;
4642451dbcc4f9641df188326215b204b798eb70c46vandwalle        } else {
4652451dbcc4f9641df188326215b204b798eb70c46vandwalle            if (choice.intValue() < 0) {
4662451dbcc4f9641df188326215b204b798eb70c46vandwalle                choice = 20; // Compatibility with older files
4672451dbcc4f9641df188326215b204b798eb70c46vandwalle            }
4682451dbcc4f9641df188326215b204b798eb70c46vandwalle            return choice.intValue();
4692451dbcc4f9641df188326215b204b798eb70c46vandwalle        }
4702451dbcc4f9641df188326215b204b798eb70c46vandwalle    }
4712451dbcc4f9641df188326215b204b798eb70c46vandwalle
4722451dbcc4f9641df188326215b204b798eb70c46vandwalle
4732451dbcc4f9641df188326215b204b798eb70c46vandwalle    int getScoreFromVisibility(WifiConfiguration.Visibility visibility, int rssiBoost) {
4742451dbcc4f9641df188326215b204b798eb70c46vandwalle        int rssiBoost5 = 0;
4752451dbcc4f9641df188326215b204b798eb70c46vandwalle        int score = 0;
4764dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
4772451dbcc4f9641df188326215b204b798eb70c46vandwalle        /**
4782451dbcc4f9641df188326215b204b798eb70c46vandwalle         * Boost RSSI value of 5GHz bands iff the base value is better than threshold
4792451dbcc4f9641df188326215b204b798eb70c46vandwalle         * This implements band preference where we prefer 5GHz if RSSI5 is good enough, whereas
4802451dbcc4f9641df188326215b204b798eb70c46vandwalle         * we prefer 2.4GHz otherwise.
4812451dbcc4f9641df188326215b204b798eb70c46vandwalle         * Note that 2.4GHz doesn't need a boost since at equal power the RSSI is typically
4822451dbcc4f9641df188326215b204b798eb70c46vandwalle         * 6-10 dB higher
4832451dbcc4f9641df188326215b204b798eb70c46vandwalle         */
4842451dbcc4f9641df188326215b204b798eb70c46vandwalle        if ((visibility.rssi5 + rssiBoost) > WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD) {
4852451dbcc4f9641df188326215b204b798eb70c46vandwalle            rssiBoost5 = 25;
4862451dbcc4f9641df188326215b204b798eb70c46vandwalle        } else if ((visibility.rssi5 + rssiBoost)
4872451dbcc4f9641df188326215b204b798eb70c46vandwalle                > WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD_LOW) {
4882451dbcc4f9641df188326215b204b798eb70c46vandwalle            rssiBoost5 = 15;
4892451dbcc4f9641df188326215b204b798eb70c46vandwalle        }
4902451dbcc4f9641df188326215b204b798eb70c46vandwalle
4912451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Select which band to use so as to score a
4922451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (visibility.rssi5 + rssiBoost5 > visibility.rssi24) {
4932451dbcc4f9641df188326215b204b798eb70c46vandwalle            // Prefer a's 5GHz
4942451dbcc4f9641df188326215b204b798eb70c46vandwalle            score = visibility.rssi5 + rssiBoost5 + rssiBoost;
4952451dbcc4f9641df188326215b204b798eb70c46vandwalle        } else {
4962451dbcc4f9641df188326215b204b798eb70c46vandwalle            // Prefer a's 2.4GHz
4972451dbcc4f9641df188326215b204b798eb70c46vandwalle            score = visibility.rssi24 + rssiBoost;
498f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
4992451dbcc4f9641df188326215b204b798eb70c46vandwalle
5002451dbcc4f9641df188326215b204b798eb70c46vandwalle        return score;
501f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
502f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
5032451dbcc4f9641df188326215b204b798eb70c46vandwalle    // Compare WifiConfiguration by RSSI, and return a comparison value in the range [-50, +50]
5042451dbcc4f9641df188326215b204b798eb70c46vandwalle    // The result represents "approximately" an RSSI difference measured in dBM
5052451dbcc4f9641df188326215b204b798eb70c46vandwalle    // Adjusted with various parameters:
5062451dbcc4f9641df188326215b204b798eb70c46vandwalle    // +) current network gets a +15 boost
5072451dbcc4f9641df188326215b204b798eb70c46vandwalle    // +) 5GHz signal, if they are strong enough, get a +15 or +25 boost, representing the
5082451dbcc4f9641df188326215b204b798eb70c46vandwalle    // fact that at short range we prefer 5GHz band as it is cleaner of interference and
5092451dbcc4f9641df188326215b204b798eb70c46vandwalle    // provides for wider channels
5102451dbcc4f9641df188326215b204b798eb70c46vandwalle    int compareWifiConfigurationsRSSI(WifiConfiguration a, WifiConfiguration b,
5112451dbcc4f9641df188326215b204b798eb70c46vandwalle                                      String currentConfiguration) {
512f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        int order = 0;
5132451dbcc4f9641df188326215b204b798eb70c46vandwalle
5142451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Boost used so as to favor current config
5152451dbcc4f9641df188326215b204b798eb70c46vandwalle        int aRssiBoost = 0;
5162451dbcc4f9641df188326215b204b798eb70c46vandwalle        int bRssiBoost = 0;
5172451dbcc4f9641df188326215b204b798eb70c46vandwalle
5182451dbcc4f9641df188326215b204b798eb70c46vandwalle        int scoreA;
5192451dbcc4f9641df188326215b204b798eb70c46vandwalle        int scoreB;
5202451dbcc4f9641df188326215b204b798eb70c46vandwalle
5212451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Retrieve the visibility
522f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        WifiConfiguration.Visibility astatus = a.visibility;
523f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        WifiConfiguration.Visibility bstatus = b.visibility;
524f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (astatus == null || bstatus == null) {
5252451dbcc4f9641df188326215b204b798eb70c46vandwalle            // Error visibility wasn't set
526f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            logDbg("compareWifiConfigurations NULL band status!");
527f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            return 0;
528f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
5292451dbcc4f9641df188326215b204b798eb70c46vandwalle
5302451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Apply Hysteresis, boost RSSI of current configuration
5312451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (null != currentConfiguration) {
5322451dbcc4f9641df188326215b204b798eb70c46vandwalle            if (a.configKey().equals(currentConfiguration)) {
533c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle                aRssiBoost += 20;
5342451dbcc4f9641df188326215b204b798eb70c46vandwalle            } else if (b.configKey().equals(currentConfiguration)) {
535c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle                bRssiBoost += 20;
5362451dbcc4f9641df188326215b204b798eb70c46vandwalle            }
5372451dbcc4f9641df188326215b204b798eb70c46vandwalle        }
5382451dbcc4f9641df188326215b204b798eb70c46vandwalle
5392451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (VDBG)  {
5402451dbcc4f9641df188326215b204b798eb70c46vandwalle            logDbg("compareWifiConfigurationsRSSI: " + a.configKey()
541c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    + " " + Integer.toString(astatus.rssi24)
542c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    + "," + Integer.toString(astatus.rssi5)
5432451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + " boost=" + Integer.toString(aRssiBoost)
544c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    + " " + b.configKey() + " "
545c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    + Integer.toString(bstatus.rssi24) + ","
546c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    + Integer.toString(bstatus.rssi5)
5472451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + " boost=" + Integer.toString(bRssiBoost)
5482451dbcc4f9641df188326215b204b798eb70c46vandwalle            );
549f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
5502451dbcc4f9641df188326215b204b798eb70c46vandwalle
5512451dbcc4f9641df188326215b204b798eb70c46vandwalle        scoreA = getScoreFromVisibility(astatus, aRssiBoost);
5522451dbcc4f9641df188326215b204b798eb70c46vandwalle        scoreB = getScoreFromVisibility(bstatus, bRssiBoost);
5532451dbcc4f9641df188326215b204b798eb70c46vandwalle
5542451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Compare a and b
5552451dbcc4f9641df188326215b204b798eb70c46vandwalle        // If a score is higher then a > b and the order is descending (negative)
5562451dbcc4f9641df188326215b204b798eb70c46vandwalle        // If b score is higher then a < b and the order is ascending (positive)
5572451dbcc4f9641df188326215b204b798eb70c46vandwalle        order = scoreB - scoreA;
5582451dbcc4f9641df188326215b204b798eb70c46vandwalle
5592451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Normalize the order to [-50, +50]
5602451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (order > 50) order = 50;
5612451dbcc4f9641df188326215b204b798eb70c46vandwalle        else if (order < -50) order = -50;
5622451dbcc4f9641df188326215b204b798eb70c46vandwalle
5632451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (VDBG) {
5642451dbcc4f9641df188326215b204b798eb70c46vandwalle            String prefer = " = ";
5652451dbcc4f9641df188326215b204b798eb70c46vandwalle            if (order > 0) {
5662451dbcc4f9641df188326215b204b798eb70c46vandwalle                prefer = " < "; // Ascending
5672451dbcc4f9641df188326215b204b798eb70c46vandwalle            } else if (order < 0) {
5682451dbcc4f9641df188326215b204b798eb70c46vandwalle                prefer = " > "; // Descending
5692451dbcc4f9641df188326215b204b798eb70c46vandwalle            }
5702451dbcc4f9641df188326215b204b798eb70c46vandwalle            logDbg("compareWifiConfigurationsRSSI " + a.configKey()
5712451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + " rssi=(" + a.visibility.rssi24
5722451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + "," + a.visibility.rssi5
5732451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + ") num=(" + a.visibility.num24
5742451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + "," + a.visibility.num5 + ")"
5752451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + prefer + b.configKey()
5762451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + " rssi=(" + b.visibility.rssi24
5772451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + "," + b.visibility.rssi5
5782451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + ") num=(" + b.visibility.num24
5792451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + "," + b.visibility.num5 + ")"
5802451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + " -> " + order);
5812451dbcc4f9641df188326215b204b798eb70c46vandwalle        }
5822451dbcc4f9641df188326215b204b798eb70c46vandwalle
583f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        return order;
584f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
585f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
5864dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
5872451dbcc4f9641df188326215b204b798eb70c46vandwalle    int compareWifiConfigurationsWithScorer(WifiConfiguration a, WifiConfiguration b) {
5882451dbcc4f9641df188326215b204b798eb70c46vandwalle
5892451dbcc4f9641df188326215b204b798eb70c46vandwalle        int aRssiBoost = 0;
5902451dbcc4f9641df188326215b204b798eb70c46vandwalle        int bRssiBoost = 0;
5912451dbcc4f9641df188326215b204b798eb70c46vandwalle
5922451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Apply Hysteresis : boost RSSI of current configuration before
5932451dbcc4f9641df188326215b204b798eb70c46vandwalle        // looking up the score
5942451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (null != mCurrentConfigurationKey) {
5952451dbcc4f9641df188326215b204b798eb70c46vandwalle            if (a.configKey().equals(mCurrentConfigurationKey)) {
596c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle                aRssiBoost += 20;
5972451dbcc4f9641df188326215b204b798eb70c46vandwalle            } else if (b.configKey().equals(mCurrentConfigurationKey)) {
598c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle                bRssiBoost += 20;
5992451dbcc4f9641df188326215b204b798eb70c46vandwalle            }
6002451dbcc4f9641df188326215b204b798eb70c46vandwalle        }
6012451dbcc4f9641df188326215b204b798eb70c46vandwalle        int scoreA = getConfigNetworkScore(a, 3000, aRssiBoost);
602ede507649471f1113e9e1919812115ca5a6bc0c8vandwalle        int scoreB = getConfigNetworkScore(b, 3000, bRssiBoost);
6032451dbcc4f9641df188326215b204b798eb70c46vandwalle
6042451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Both configurations need to have a score for the scorer to be used
6052451dbcc4f9641df188326215b204b798eb70c46vandwalle        // ...and the scores need to be different:-)
6062451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (scoreA == WifiNetworkScoreCache.INVALID_NETWORK_SCORE
6072451dbcc4f9641df188326215b204b798eb70c46vandwalle                || scoreB == WifiNetworkScoreCache.INVALID_NETWORK_SCORE) {
6082451dbcc4f9641df188326215b204b798eb70c46vandwalle            return 0;
6092451dbcc4f9641df188326215b204b798eb70c46vandwalle        }
6102451dbcc4f9641df188326215b204b798eb70c46vandwalle
6112451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (VDBG) {
6122451dbcc4f9641df188326215b204b798eb70c46vandwalle            String prefer = " = ";
6132451dbcc4f9641df188326215b204b798eb70c46vandwalle            if (scoreA < scoreB) {
6142451dbcc4f9641df188326215b204b798eb70c46vandwalle                prefer = " < ";
6152451dbcc4f9641df188326215b204b798eb70c46vandwalle            } if (scoreA > scoreB) {
6162451dbcc4f9641df188326215b204b798eb70c46vandwalle                prefer = " > ";
6172451dbcc4f9641df188326215b204b798eb70c46vandwalle            }
6182451dbcc4f9641df188326215b204b798eb70c46vandwalle            logDbg("compareWifiConfigurationsWithScorer " + a.configKey()
6192451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + " rssi=(" + a.visibility.rssi24
6202451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + "," + a.visibility.rssi5
6212451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + ") num=(" + a.visibility.num24
6222451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + "," + a.visibility.num5 + ")"
6232451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + prefer + b.configKey()
6242451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + " rssi=(" + b.visibility.rssi24
6252451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + "," + b.visibility.rssi5
6262451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + ") num=(" + b.visibility.num24
6272451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + "," + b.visibility.num5 + ")"
6282451dbcc4f9641df188326215b204b798eb70c46vandwalle                    + " -> " + Integer.toString(scoreB - scoreA));
6292451dbcc4f9641df188326215b204b798eb70c46vandwalle        }
630c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle
6312451dbcc4f9641df188326215b204b798eb70c46vandwalle        // If scoreA > scoreB, the comparison is descending hence the return value is negative
6322451dbcc4f9641df188326215b204b798eb70c46vandwalle        return scoreB - scoreA;
6332451dbcc4f9641df188326215b204b798eb70c46vandwalle    }
6342451dbcc4f9641df188326215b204b798eb70c46vandwalle
635f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    int compareWifiConfigurations(WifiConfiguration a, WifiConfiguration b) {
636f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        int order = 0;
637117be607246604e875de62aa8cdd99700b77a2b4vandwalle        String lastSelectedConfiguration = mWifiConfigStore.getLastSelectedConfiguration();
638f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        boolean linked = false;
639f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
640453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle        if ((a.linkedConfigurations != null) && (b.linkedConfigurations != null)
641453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                && (a.autoJoinStatus == WifiConfiguration.AUTO_JOIN_ENABLED)
642453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                && (b.autoJoinStatus == WifiConfiguration.AUTO_JOIN_ENABLED)) {
6432451dbcc4f9641df188326215b204b798eb70c46vandwalle            if ((a.linkedConfigurations.get(b.configKey(true)) != null)
6442451dbcc4f9641df188326215b204b798eb70c46vandwalle                    && (b.linkedConfigurations.get(a.configKey(true)) != null)) {
645f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                linked = true;
646f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
647f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
648f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
649f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (a.ephemeral && b.ephemeral == false) {
650f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (VDBG) {
651453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                logDbg("compareWifiConfigurations ephemeral and prefers " + b.configKey()
652453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                        + " over " + a.configKey());
653f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
654931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            return 1; // b is of higher priority - ascending
655f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
656f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (b.ephemeral && a.ephemeral == false) {
657f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (VDBG) {
6582451dbcc4f9641df188326215b204b798eb70c46vandwalle                logDbg("compareWifiConfigurations ephemeral and prefers " + a.configKey()
659453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                        + " over " + b.configKey());
660f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
661931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            return -1; // a is of higher priority - descending
662f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
663f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
6642451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Apply RSSI, in the range [-5, +5]
6652451dbcc4f9641df188326215b204b798eb70c46vandwalle        // after band adjustment, +n difference roughly corresponds to +10xn dBm
6662451dbcc4f9641df188326215b204b798eb70c46vandwalle        order = order + compareWifiConfigurationsRSSI(a, b, mCurrentConfigurationKey);
667f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
6682451dbcc4f9641df188326215b204b798eb70c46vandwalle        // If the configurations are not linked, compare by user's choice, only a
6692451dbcc4f9641df188326215b204b798eb70c46vandwalle        // very high RSSI difference can then override the choice
6702451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (!linked) {
6712451dbcc4f9641df188326215b204b798eb70c46vandwalle            int choice;
672f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
6732451dbcc4f9641df188326215b204b798eb70c46vandwalle            choice = getConnectChoice(a, b);
6742451dbcc4f9641df188326215b204b798eb70c46vandwalle            if (choice > 0) {
675931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // a is of higher priority - descending
6762451dbcc4f9641df188326215b204b798eb70c46vandwalle                order = order - choice;
677f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                if (VDBG) {
6782451dbcc4f9641df188326215b204b798eb70c46vandwalle                    logDbg("compareWifiConfigurations prefers " + a.configKey()
679453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                            + " over " + b.configKey()
6802451dbcc4f9641df188326215b204b798eb70c46vandwalle                            + " due to user choice order -> " + Integer.toString(order));
681f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                }
6822451dbcc4f9641df188326215b204b798eb70c46vandwalle            }
6832451dbcc4f9641df188326215b204b798eb70c46vandwalle
6842451dbcc4f9641df188326215b204b798eb70c46vandwalle            choice = getConnectChoice(b, a);
6852451dbcc4f9641df188326215b204b798eb70c46vandwalle            if (choice > 0) {
686931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // a is of lower priority - ascending
6872451dbcc4f9641df188326215b204b798eb70c46vandwalle                order = order + choice;
6884dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                if (VDBG) {
6892451dbcc4f9641df188326215b204b798eb70c46vandwalle                    logDbg("compareWifiConfigurations prefers " + b.configKey() + " over "
6902451dbcc4f9641df188326215b204b798eb70c46vandwalle                            + a.configKey() + " due to user choice order ->"
6912451dbcc4f9641df188326215b204b798eb70c46vandwalle                            + Integer.toString(order));
692f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                }
693f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
694f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
695ede1310be531a84faa08f02c3fd243448dd936ddvandwalle
696f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (order == 0) {
697931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // We don't know anything - pick the last seen i.e. K behavior
698931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // we should do this only for recently picked configurations
699f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (a.priority > b.priority) {
700931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // a is of higher priority - descending
7012451dbcc4f9641df188326215b204b798eb70c46vandwalle                if (VDBG) {
702453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                    logDbg("compareWifiConfigurations prefers -1 " + a.configKey() + " over "
703453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                            + b.configKey() + " due to priority");
704f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                }
705f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
706f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                order = -1;
707f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            } else if (a.priority < b.priority) {
708931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // a is of lower priority - ascending
7092451dbcc4f9641df188326215b204b798eb70c46vandwalle                if (VDBG) {
710453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                    logDbg("compareWifiConfigurations prefers +1 " + b.configKey() + " over "
711453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                            + a.configKey() + " due to priority");
712f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                }
7132451dbcc4f9641df188326215b204b798eb70c46vandwalle                order = 1;
714f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
715f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
716f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
717f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        String sorder = " == ";
718931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        if (order > 0) {
719f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            sorder = " < ";
720931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        } else if (order < 0) {
721f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            sorder = " > ";
722931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        }
723f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
7242451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (VDBG) {
725453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle            logDbg("compareWifiConfigurations Done: " + a.configKey() + sorder
726453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                    + b.configKey() + " order " + Integer.toString(order));
727f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
728f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
729f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        return order;
730f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
731f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
732c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle    boolean isBadCandidate(int rssi5, int rssi24) {
733c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle        return (rssi5 < -80 && rssi24 < -90);
734c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle    }
735c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle
736c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle    int compareWifiConfigurationsTop(WifiConfiguration a, WifiConfiguration b) {
737c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle        int scorerOrder = compareWifiConfigurationsWithScorer(a, b);
738c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle        int order = compareWifiConfigurations(a, b);
739c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle
740c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle        if (scorerOrder * order < 0) {
741c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle            // For debugging purpose, remember that an override happened
742c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle            // during that autojoin Attempt
743c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle            didOverride = true;
744c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle            a.numScorerOverride++;
745c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle            b.numScorerOverride++;
746c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle        }
747c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle
748c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle        if (scorerOrder != 0) {
749c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle            // If the scorer came up with a result then use the scorer's result, else use
750c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle            // the order provided by the base comparison function
751c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle            order = scorerOrder;
752c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle        }
753c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle        return order;
754c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle    }
755c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle
756c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle        /**
757c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle         * attemptRoam function implements the core of the same SSID switching algorithm
758c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle         */
759b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle    ScanResult attemptRoam(WifiConfiguration current, int age) {
7604dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        ScanResult a = null;
761b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        if (current == null) {
762b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            if (VDBG)   {
763b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                logDbg("attemptRoam not associated");
764b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            }
7654dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            return null;
7664dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        }
767b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        if (current.scanResultCache == null) {
768b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            if (VDBG)   {
769b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                logDbg("attemptRoam no scan cache");
770b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            }
7714dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            return null;
7724dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        }
773b07da189850a4bfa268f8ab9be7867935eb2ecb5vandwalle        if (current.scanResultCache.size() > 6) {
774b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            if (VDBG)   {
775c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                logDbg("attemptRoam scan cache size "
776c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                        + current.scanResultCache.size() + " --> bail");
777b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            }
778931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // Implement same SSID roaming only for configurations
779c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle            // that have less than 4 BSSIDs
7804dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            return null;
7814dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        }
782b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        String currentBSSID = mWifiStateMachine.getCurrentBSSID();
783b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        if (currentBSSID == null) {
784b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            if (DBG)   {
785b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                logDbg("attemptRoam currentBSSID unknown");
786b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            }
787b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            return null;
788b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        }
789b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle
790b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        if (current.bssidOwnerUid!= 0 && current.bssidOwnerUid != Process.WIFI_UID) {
791b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            if (DBG)   {
792c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                logDbg("attemptRoam BSSID owner is "
793c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                        + Long.toString(current.bssidOwnerUid) + " -> bail");
794b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            }
7954dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            return null;
7964dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        }
7974dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
798931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        // Determine which BSSID we want to associate to, taking account
799c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle        // relative strength of 5 and 2.4 GHz BSSIDs
8002451dbcc4f9641df188326215b204b798eb70c46vandwalle        long nowMs = System.currentTimeMillis();
8014dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        int bRssiBoost5 = 0;
8024dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        int aRssiBoost5 = 0;
8034dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        int bRssiBoost = 0;
8044dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        int aRssiBoost = 0;
805b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        for (ScanResult b : current.scanResultCache.values()) {
8064dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
807931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            if ((b.seen == 0) || (b.BSSID == null)
8082451dbcc4f9641df188326215b204b798eb70c46vandwalle                    || (nowMs - b.seen) > age ) {
8092451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // TODO: do not apply blacklisting right now so as to leave this
8102451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // bug as apparent
8112451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // https://b2.corp.google.com/#/issues/16504012
8122451dbcc4f9641df188326215b204b798eb70c46vandwalle                    //                    || b.status != ScanResult.ENABLED) {
8134dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                continue;
8143a2a3d226881cce8a4e511302231d843b0def303vandwalle            }
8154dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
816931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // Pick first one
8174dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            if (a == null) {
8184dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                a = b;
8194dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                continue;
8204dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            }
8214dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
8222451dbcc4f9641df188326215b204b798eb70c46vandwalle            // Apply hysteresis: we favor the currentBSSID by giving it a boost
823b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            if (currentBSSID.equals(b.BSSID)) {
824931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // Reduce the benefit of hysteresis if RSSI <= -75
825c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                if (b.level <= WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD) {
826c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    bRssiBoost = +6;
827c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                } else {
828c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    bRssiBoost = +10;
829c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                }
8304dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            }
831b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            if (currentBSSID.equals(a.BSSID)) {
832c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                if (a.level <= WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD) {
833931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                    // Reduce the benefit of hysteresis if RSSI <= -75
834c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    aRssiBoost = +6;
835c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                } else {
836c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    aRssiBoost = +10;
837c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                }
8384dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            }
8392451dbcc4f9641df188326215b204b798eb70c46vandwalle
8402451dbcc4f9641df188326215b204b798eb70c46vandwalle            // Favor 5GHz: give a boost to 5GHz BSSIDs
8412451dbcc4f9641df188326215b204b798eb70c46vandwalle            //   Boost the BSSID if it is on 5GHz, above a threshold
8422451dbcc4f9641df188326215b204b798eb70c46vandwalle            //   But penalize it if it is on 5GHz and below threshold
843c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle            if (b.is5GHz() && (b.level+bRssiBoost)
844c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    > WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD) {
8454dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                bRssiBoost5 = 25;
846c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle            } else if (b.is5GHz() && (b.level+bRssiBoost)
847c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    < WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD) {
848c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                bRssiBoost5 = -10;
8494dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            }
850c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle            if (a.is5GHz() && (a.level+aRssiBoost)
851c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    > WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD) {
8524dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                aRssiBoost5 = 25;
853c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle            } else if (a.is5GHz() && (a.level+aRssiBoost)
854c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    < WifiConfiguration.G_BAND_PREFERENCE_RSSI_THRESHOLD) {
855c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                aRssiBoost5 = -10;
8564dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            }
8574dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
8584dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            if (VDBG)  {
859c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                String comp = " < ";
860c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                if (b.level + bRssiBoost + bRssiBoost5 > a.level +aRssiBoost + aRssiBoost5) {
861c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    comp = " > ";
862c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                }
8634dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                logDbg("attemptRoam: "
864c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                        + b.BSSID + " rssi=" + b.level + " boost=" + Integer.toString(bRssiBoost)
865c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                        + "/" + Integer.toString(bRssiBoost5) + " freq=" + b.frequency + comp
866c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                        + a.BSSID + " rssi=" + a.level + " boost=" + Integer.toString(aRssiBoost)
867c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                        + "/" + Integer.toString(aRssiBoost5) + " freq=" + a.frequency);
868c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle            }
869c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle
8702451dbcc4f9641df188326215b204b798eb70c46vandwalle            // Compare the RSSIs after applying the hysteresis boost and the 5GHz
8712451dbcc4f9641df188326215b204b798eb70c46vandwalle            // boost if applicable
872c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle            if (b.level + bRssiBoost + bRssiBoost5 > a.level +aRssiBoost + aRssiBoost5) {
873931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // b is the better BSSID
874c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                a = b;
8754dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            }
8764dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        }
8773a2a3d226881cce8a4e511302231d843b0def303vandwalle        if (a != null) {
8783a2a3d226881cce8a4e511302231d843b0def303vandwalle            if (VDBG)  {
8793a2a3d226881cce8a4e511302231d843b0def303vandwalle                logDbg("attemptRoam: Found "
8803a2a3d226881cce8a4e511302231d843b0def303vandwalle                        + a.BSSID + " rssi=" + a.level + " freq=" + a.frequency
8813a2a3d226881cce8a4e511302231d843b0def303vandwalle                        + " Current: " + currentBSSID);
8823a2a3d226881cce8a4e511302231d843b0def303vandwalle            }
8833a2a3d226881cce8a4e511302231d843b0def303vandwalle            if (currentBSSID.equals(a.BSSID)) {
8843a2a3d226881cce8a4e511302231d843b0def303vandwalle                return null;
8853a2a3d226881cce8a4e511302231d843b0def303vandwalle            }
8864dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle        }
8873a2a3d226881cce8a4e511302231d843b0def303vandwalle        return a;
8884dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle    }
8894dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
890931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle    /**
8912451dbcc4f9641df188326215b204b798eb70c46vandwalle     * getNetworkScore()
8922451dbcc4f9641df188326215b204b798eb70c46vandwalle     *
8932451dbcc4f9641df188326215b204b798eb70c46vandwalle     * if scorer is present, get the network score of a WifiConfiguration
8942451dbcc4f9641df188326215b204b798eb70c46vandwalle     *
8952451dbcc4f9641df188326215b204b798eb70c46vandwalle     * Note: this should be merge with setVisibility
8962451dbcc4f9641df188326215b204b798eb70c46vandwalle     *
8972451dbcc4f9641df188326215b204b798eb70c46vandwalle     * @param config
8982451dbcc4f9641df188326215b204b798eb70c46vandwalle     * @return score
899c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle     * @return score
9002451dbcc4f9641df188326215b204b798eb70c46vandwalle     */
9012451dbcc4f9641df188326215b204b798eb70c46vandwalle    int getConfigNetworkScore(WifiConfiguration config, int age, int rssiBoost) {
9022451dbcc4f9641df188326215b204b798eb70c46vandwalle
9032451dbcc4f9641df188326215b204b798eb70c46vandwalle        int score = WifiNetworkScoreCache.INVALID_NETWORK_SCORE;
9042451dbcc4f9641df188326215b204b798eb70c46vandwalle        if (mNetworkScoreCache == null) {
9052451dbcc4f9641df188326215b204b798eb70c46vandwalle            return score;
9062451dbcc4f9641df188326215b204b798eb70c46vandwalle        }
9072451dbcc4f9641df188326215b204b798eb70c46vandwalle
9082451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Get current date
9092451dbcc4f9641df188326215b204b798eb70c46vandwalle        long nowMs = System.currentTimeMillis();
9102451dbcc4f9641df188326215b204b798eb70c46vandwalle
9112451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Run thru all cached scan results
9122451dbcc4f9641df188326215b204b798eb70c46vandwalle        for (ScanResult result : scanResultCache.values()) {
9132451dbcc4f9641df188326215b204b798eb70c46vandwalle            if ((nowMs - result.seen) < age) {
9142451dbcc4f9641df188326215b204b798eb70c46vandwalle                int sc = mNetworkScoreCache.getNetworkScore(result, rssiBoost);
9152451dbcc4f9641df188326215b204b798eb70c46vandwalle                if (sc > score) {
9162451dbcc4f9641df188326215b204b798eb70c46vandwalle                    score = sc;
9172451dbcc4f9641df188326215b204b798eb70c46vandwalle                }
9182451dbcc4f9641df188326215b204b798eb70c46vandwalle            }
9192451dbcc4f9641df188326215b204b798eb70c46vandwalle        }
9202451dbcc4f9641df188326215b204b798eb70c46vandwalle        return score;
9212451dbcc4f9641df188326215b204b798eb70c46vandwalle    }
9222451dbcc4f9641df188326215b204b798eb70c46vandwalle
9232451dbcc4f9641df188326215b204b798eb70c46vandwalle    /**
924931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle     * attemptAutoJoin() function implements the core of the a network switching algorithm
925931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle     */
926f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    void attemptAutoJoin() {
927c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle        didOverride = false;
928b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        int networkSwitchType = AUTO_JOIN_IDLE;
9294dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
9308c9088d11880553458f09377cc60d6eb7e66747bvandwalle        String lastSelectedConfiguration = mWifiConfigStore.getLastSelectedConfiguration();
9318c9088d11880553458f09377cc60d6eb7e66747bvandwalle
932931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        // Reset the currentConfiguration Key, and set it only if WifiStateMachine and
933453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle        // supplicant agree
934453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle        mCurrentConfigurationKey = null;
935453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle        WifiConfiguration currentConfiguration = mWifiStateMachine.getCurrentWifiConfiguration();
936453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle
937f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        WifiConfiguration candidate = null;
938f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
939931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        // Obtain the subset of recently seen networks
94027355a942653264388e909a4276196ee63e57811vandwalle        List<WifiConfiguration> list = mWifiConfigStore.getRecentConfiguredNetworks(3000, false);
941f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (list == null) {
942f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (VDBG)  logDbg("attemptAutoJoin nothing");
943f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            return;
944f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
945f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
946931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        // Find the currently connected network: ask the supplicant directly
947f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        String val = mWifiNative.status();
948f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        String status[] = val.split("\\r?\\n");
949f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (VDBG) {
950f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            logDbg("attemptAutoJoin() status=" + val + " split="
951f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    + Integer.toString(status.length));
952f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
953f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
954b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle        int supplicantNetId = -1;
955f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        for (String key : status) {
956f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (key.regionMatches(0, "id=", 0, 3)) {
957f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                int idx = 3;
958b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle                supplicantNetId = 0;
959f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                while (idx < key.length()) {
960f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    char c = key.charAt(idx);
961f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
962f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    if ((c >= 0x30) && (c <= 0x39)) {
963b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle                        supplicantNetId *= 10;
964b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle                        supplicantNetId += c - 0x30;
965f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                        idx++;
966f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    } else {
967f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                        break;
968f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    }
969f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                }
970f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
971f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
972ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle        if (DBG) {
973ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle            logDbg("attemptAutoJoin() num recent config " + Integer.toString(list.size())
974b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle                    + " ---> suppId=" + Integer.toString(supplicantNetId));
975ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle        }
976f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
977453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle        if (currentConfiguration != null) {
9782451dbcc4f9641df188326215b204b798eb70c46vandwalle            if (supplicantNetId != currentConfiguration.networkId
9792451dbcc4f9641df188326215b204b798eb70c46vandwalle                    //https://b.corp.google.com/issue?id=16484607
9802451dbcc4f9641df188326215b204b798eb70c46vandwalle                    //mark this confition as an error only if the mismatched networkId are valid
9812451dbcc4f9641df188326215b204b798eb70c46vandwalle                    && supplicantNetId != WifiConfiguration.INVALID_NETWORK_ID
9822451dbcc4f9641df188326215b204b798eb70c46vandwalle                    && currentConfiguration.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
983453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                logDbg("attemptAutoJoin() ERROR wpa_supplicant out of sync nid="
984b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle                        + Integer.toString(supplicantNetId) + " WifiStateMachine="
985453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                        + Integer.toString(currentConfiguration.networkId));
986b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                mWifiStateMachine.disconnectCommand();
987b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                return;
988453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle            } else {
989453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                mCurrentConfigurationKey = currentConfiguration.configKey();
990453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle            }
991453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle        }
992453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle
993b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle        int currentNetId = -1;
994b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle        if (currentConfiguration != null) {
995931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // If we are associated to a configuration, it will
996b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle            // be compared thru the compareNetwork function
997b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle            currentNetId = currentConfiguration.networkId;
998b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle        }
999b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle
1000931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        /**
1001931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle         * Run thru all visible configurations without looking at the one we
1002c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle         * are currently associated to
10034dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle         * select Best Network candidate from known WifiConfigurations
1004931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle         */
1005f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        for (WifiConfiguration config : list) {
1006f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if ((config.status == WifiConfiguration.Status.DISABLED)
1007f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    && (config.disableReason == WifiConfiguration.DISABLED_AUTH_FAILURE)) {
1008ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                if (DBG) {
1009b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                    logDbg("attemptAutoJoin skip candidate due to auth failure: "
1010ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                            + config.configKey(true));
1011ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                }
1012f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                continue;
1013f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
1014453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle
1015e86c962bb99a8b126ed64ddcc6b112161549e26dvandwalle            if (config.SSID == null) {
1016b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                continue;
1017e86c962bb99a8b126ed64ddcc6b112161549e26dvandwalle            }
1018e86c962bb99a8b126ed64ddcc6b112161549e26dvandwalle
101927355a942653264388e909a4276196ee63e57811vandwalle            if (config.autoJoinStatus >=
102027355a942653264388e909a4276196ee63e57811vandwalle                    WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
1021931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // Avoid networks disabled because of AUTH failure altogether
1022ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                if (DBG) {
1023ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                    logDbg("attemptAutoJoin skip candidate due to auto join status "
1024ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                            + Integer.toString(config.autoJoinStatus) + " key "
1025ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                            + config.configKey(true));
1026ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                }
1027f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                continue;
1028f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
1029f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
1030931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // Try to un-blacklist based on elapsed time
103127355a942653264388e909a4276196ee63e57811vandwalle            if (config.blackListTimestamp > 0) {
103227355a942653264388e909a4276196ee63e57811vandwalle                long now = System.currentTimeMillis();
103327355a942653264388e909a4276196ee63e57811vandwalle                if (now < config.blackListTimestamp) {
1034931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                    /**
1035931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                     * looks like there was a change in the system clock since we black listed, and
1036931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                     * timestamp is not meaningful anymore, hence lose it.
1037931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                     * this event should be rare enough so that we still want to lose the black list
10382451dbcc4f9641df188326215b204b798eb70c46vandwalle                     */
103927355a942653264388e909a4276196ee63e57811vandwalle                    config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
104027355a942653264388e909a4276196ee63e57811vandwalle                } else {
10414dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                    if ((now - config.blackListTimestamp) > loseBlackListHardMilli) {
1042931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                        // Reenable it after 18 hours, i.e. next day
104327355a942653264388e909a4276196ee63e57811vandwalle                        config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
10444dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                    } else if ((now - config.blackListTimestamp) > loseBlackListSoftMilli) {
1045931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                        // Lose blacklisting due to bad link
104627355a942653264388e909a4276196ee63e57811vandwalle                        config.setAutoJoinStatus(config.autoJoinStatus - 8);
104727355a942653264388e909a4276196ee63e57811vandwalle                    }
104827355a942653264388e909a4276196ee63e57811vandwalle                }
104927355a942653264388e909a4276196ee63e57811vandwalle            }
105027355a942653264388e909a4276196ee63e57811vandwalle
1051931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // Try to unblacklist based on good visibility
105227355a942653264388e909a4276196ee63e57811vandwalle            if (config.visibility.rssi5 < WifiConfiguration.UNBLACKLIST_THRESHOLD_5_SOFT
105327355a942653264388e909a4276196ee63e57811vandwalle                    && config.visibility.rssi24 < WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT) {
105427355a942653264388e909a4276196ee63e57811vandwalle                if (DBG) {
1055c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    logDbg("attemptAutoJoin do not unblacklist due to low visibility "
10564dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + config.autoJoinStatus
10574dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + " key " + config.configKey(true)
10584dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + " rssi=(" + config.visibility.rssi24
10594dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + "," + config.visibility.rssi5
10604dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + ") num=(" + config.visibility.num24
10614dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + "," + config.visibility.num5 + ")");
106227355a942653264388e909a4276196ee63e57811vandwalle                }
106327355a942653264388e909a4276196ee63e57811vandwalle            } else if (config.visibility.rssi5 < WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD
106427355a942653264388e909a4276196ee63e57811vandwalle                    && config.visibility.rssi24 < WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD) {
1065931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // If the network is simply temporary disabled, don't allow reconnect until
1066931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // RSSI becomes good enough
106727355a942653264388e909a4276196ee63e57811vandwalle                config.setAutoJoinStatus(config.autoJoinStatus - 1);
106827355a942653264388e909a4276196ee63e57811vandwalle                if (DBG) {
106927355a942653264388e909a4276196ee63e57811vandwalle                    logDbg("attemptAutoJoin good candidate seen, bumped soft -> status="
107027355a942653264388e909a4276196ee63e57811vandwalle                            + config.autoJoinStatus
10714dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + " key " + config.configKey(true) + " rssi=("
10724dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + config.visibility.rssi24 + "," + config.visibility.rssi5
10734dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + ") num=(" + config.visibility.num24
10744dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + "," + config.visibility.num5 + ")");
107527355a942653264388e909a4276196ee63e57811vandwalle                }
107627355a942653264388e909a4276196ee63e57811vandwalle            } else {
1077c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                config.setAutoJoinStatus(config.autoJoinStatus - 3);
107827355a942653264388e909a4276196ee63e57811vandwalle                if (DBG) {
107927355a942653264388e909a4276196ee63e57811vandwalle                    logDbg("attemptAutoJoin good candidate seen, bumped hard -> status="
108027355a942653264388e909a4276196ee63e57811vandwalle                            + config.autoJoinStatus
10814dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + " key " + config.configKey(true) + " rssi=("
10824dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + config.visibility.rssi24 + "," + config.visibility.rssi5
10834dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + ") num=(" + config.visibility.num24
10844dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + "," + config.visibility.num5 + ")");
108527355a942653264388e909a4276196ee63e57811vandwalle                }
108627355a942653264388e909a4276196ee63e57811vandwalle            }
108727355a942653264388e909a4276196ee63e57811vandwalle
108827355a942653264388e909a4276196ee63e57811vandwalle            if (config.autoJoinStatus >=
108927355a942653264388e909a4276196ee63e57811vandwalle                    WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED) {
1090931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                // Network is blacklisted, skip
10914dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                if (DBG) {
10924dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                    logDbg("attemptAutoJoin skip blacklisted -> status="
10934dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + config.autoJoinStatus
10944dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + " key " + config.configKey(true) + " rssi=("
10954dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + config.visibility.rssi24 + "," + config.visibility.rssi5
10964dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + ") num=(" + config.visibility.num24
10974dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + "," + config.visibility.num5 + ")");
10984dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                }
109927355a942653264388e909a4276196ee63e57811vandwalle                continue;
110027355a942653264388e909a4276196ee63e57811vandwalle            }
1101f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (config.networkId == currentNetId) {
1102ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                if (DBG) {
110321bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                    logDbg("attemptAutoJoin skip current candidate  "
110421bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                            + Integer.toString(currentNetId)
1105ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                            + " key " + config.configKey(true));
1106ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle                }
1107f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                continue;
1108f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
1109f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
111021bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle            if (lastSelectedConfiguration == null ||
111121bc54cb37a0085b1c909cb4d55ebb12a2facefbvandwalle                    !config.configKey().equals(lastSelectedConfiguration)) {
1112c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle                // Don't try to autojoin a network that is too far but
1113c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle                // If that configuration is a user's choice however, try anyway
11148c9088d11880553458f09377cc60d6eb7e66747bvandwalle                if (config.visibility == null) {
11158c9088d11880553458f09377cc60d6eb7e66747bvandwalle                    continue;
11168c9088d11880553458f09377cc60d6eb7e66747bvandwalle                }
111727355a942653264388e909a4276196ee63e57811vandwalle                if (config.visibility.rssi5 < WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5
1118c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                        && config.visibility.rssi24
1119c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                        < WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24) {
11204dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                    if (DBG) {
1121ede507649471f1113e9e1919812115ca5a6bc0c8vandwalle                        logDbg("attemptAutoJoin skip due to low visibility -> status="
11224dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                                + config.autoJoinStatus
11234dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                                + " key " + config.configKey(true) + " rssi="
11244dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                                + config.visibility.rssi24 + ", " + config.visibility.rssi5
11254dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                                + " num=" + config.visibility.num24
11264dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                                + ", " + config.visibility.num5);
11274dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                    }
11288c9088d11880553458f09377cc60d6eb7e66747bvandwalle                    continue;
11298c9088d11880553458f09377cc60d6eb7e66747bvandwalle                }
11308c9088d11880553458f09377cc60d6eb7e66747bvandwalle            }
11318c9088d11880553458f09377cc60d6eb7e66747bvandwalle
1132ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle            if (DBG) {
1133c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                logDbg("attemptAutoJoin trying candidate id="
1134c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                        + Integer.toString(config.networkId) + " "
11354dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                        + config.SSID + " key " + config.configKey(true)
11364dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                        + " status=" + config.autoJoinStatus);
1137ed9938883ae2dade81c8be6cd6ceaef3febd5239vandwalle            }
1138f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
1139f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (candidate == null) {
1140f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                candidate = config;
1141f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            } else {
1142f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                if (VDBG)  {
1143453aee50caf7e332e77ab3d995d7c87a958e4fd4vandwalle                    logDbg("attemptAutoJoin will compare candidate  " + candidate.configKey()
11444dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle                            + " with " + config.configKey());
1145f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                }
1146c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle                int order = compareWifiConfigurationsTop(candidate, config);
11472451dbcc4f9641df188326215b204b798eb70c46vandwalle
11482451dbcc4f9641df188326215b204b798eb70c46vandwalle                // The lastSelectedConfiguration is the configuration the user has manually selected
1149c290d8dff6172d5fde7b9dfd74d3a20785dab246vandwalle                // thru WifiPicker, or that a 3rd party app asked us to connect to via the
11502451dbcc4f9641df188326215b204b798eb70c46vandwalle                // enableNetwork with disableOthers=true WifiManager API
11512451dbcc4f9641df188326215b204b798eb70c46vandwalle                // As this is a direct user choice, we strongly prefer this configuration,
11522451dbcc4f9641df188326215b204b798eb70c46vandwalle                // hence give +/-100
11532451dbcc4f9641df188326215b204b798eb70c46vandwalle                if ((lastSelectedConfiguration != null)
11542451dbcc4f9641df188326215b204b798eb70c46vandwalle                        && candidate.configKey().equals(lastSelectedConfiguration)) {
11552451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // candidate is the last selected configuration,
11562451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // so keep it above connect choices (+/-60) and
11572451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // above RSSI/scorer based selection of linked configuration (+/- 50)
11582451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // by reducing order by -100
11592451dbcc4f9641df188326215b204b798eb70c46vandwalle                    order = order - 100;
11602451dbcc4f9641df188326215b204b798eb70c46vandwalle                    if (VDBG)   {
11612451dbcc4f9641df188326215b204b798eb70c46vandwalle                        logDbg("  prefers -100 " + candidate.configKey()
11622451dbcc4f9641df188326215b204b798eb70c46vandwalle                                + " over " + config.configKey()
11632451dbcc4f9641df188326215b204b798eb70c46vandwalle                                + " because it is the last selected -> "
11642451dbcc4f9641df188326215b204b798eb70c46vandwalle                                + Integer.toString(order));
11652451dbcc4f9641df188326215b204b798eb70c46vandwalle                    }
11662451dbcc4f9641df188326215b204b798eb70c46vandwalle                } else if ((lastSelectedConfiguration != null)
11672451dbcc4f9641df188326215b204b798eb70c46vandwalle                        && config.configKey().equals(lastSelectedConfiguration)) {
11682451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // config is the last selected configuration,
11692451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // so keep it above connect choices (+/-60) and
11702451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // above RSSI/scorer based selection of linked configuration (+/- 50)
11712451dbcc4f9641df188326215b204b798eb70c46vandwalle                    // by increasing order by +100
11722451dbcc4f9641df188326215b204b798eb70c46vandwalle                    order = order + 100;
11732451dbcc4f9641df188326215b204b798eb70c46vandwalle                    if (VDBG)   {
11742451dbcc4f9641df188326215b204b798eb70c46vandwalle                        logDbg("  prefers +100 " + config.configKey()
11752451dbcc4f9641df188326215b204b798eb70c46vandwalle                                + " over " + candidate.configKey()
11762451dbcc4f9641df188326215b204b798eb70c46vandwalle                                + " because it is the last selected -> "
11772451dbcc4f9641df188326215b204b798eb70c46vandwalle                                + Integer.toString(order));
11782451dbcc4f9641df188326215b204b798eb70c46vandwalle                    }
11792451dbcc4f9641df188326215b204b798eb70c46vandwalle                }
11802451dbcc4f9641df188326215b204b798eb70c46vandwalle
1181f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                if (order > 0) {
1182931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle                    // Ascending : candidate < config
1183f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    candidate = config;
1184f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                }
1185f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
1186f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
1187f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
1188c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle        // Wait for VPN to be available on the system to make use of this code
11892451dbcc4f9641df188326215b204b798eb70c46vandwalle        // Now, go thru scan result to try finding a better untrusted network
1190f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        if (mNetworkScoreCache != null) {
1191f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            int rssi5 = WifiConfiguration.INVALID_RSSI;
1192f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            int rssi24 = WifiConfiguration.INVALID_RSSI;
1193f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            WifiConfiguration.Visibility visibility;
1194f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            if (candidate != null) {
1195f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                rssi5 = candidate.visibility.rssi5;
1196f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                rssi24 = candidate.visibility.rssi24;
1197f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
1198f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
1199931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // Get current date
12002451dbcc4f9641df188326215b204b798eb70c46vandwalle            long nowMs = System.currentTimeMillis();
1201c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            int currentScore = -10000;
1202c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            // The untrusted network with highest score
1203c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            ScanResult untrustedCandidate = null;
12042451dbcc4f9641df188326215b204b798eb70c46vandwalle            // Look for untrusted scored network only if the current candidate is bad
1205c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            if (isBadCandidate(rssi24, rssi5)) {
1206f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                for (ScanResult result : scanResultCache.values()) {
1207c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    int rssiBoost = 0;
1208c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    // We look only at untrusted networks with a valid SSID
1209c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    // A trusted result would have been looked at thru it's Wificonfiguration
1210c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    if (TextUtils.isEmpty(result.SSID) || !result.untrusted) {
1211c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                        continue;
1212c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    }
12132451dbcc4f9641df188326215b204b798eb70c46vandwalle                    if ((nowMs - result.seen) < 3000) {
1214c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                        // Increment usage count for the network
1215c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                        mWifiConnectionStatistics.incrementOrAddUntrusted(result.SSID, 0, 1);
1216c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle
1217c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                        if (lastUntrustedBSSID != null
1218c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                                && result.BSSID.equals(lastUntrustedBSSID)) {
1219c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                            // Apply a large hysteresis to the untrusted network we are connected to
1220c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                            rssiBoost = 25;
1221c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                        }
1222c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                        int score = mNetworkScoreCache.getNetworkScore(result, rssiBoost);
1223c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                        if (score != WifiNetworkScoreCache.INVALID_NETWORK_SCORE
1224c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                                && score > currentScore) {
1225c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                            // Highest score: Select this candidate
1226c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                            currentScore = score;
1227c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                            untrustedCandidate = result;
1228c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                            if (VDBG) {
1229c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                                logDbg("AutoJoinController: found untrusted candidate "
1230c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                                        + result.SSID
1231c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                                + " RSSI=" + result.level
1232c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                                + " freq=" + result.frequency
1233c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                                + " score=" + score);
1234c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                            }
1235f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                        }
1236f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                    }
1237f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle                }
1238f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
1239c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            if (untrustedCandidate != null) {
1240c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                if (lastUntrustedBSSID == null
1241c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                        || !untrustedCandidate.SSID.equals(lastUntrustedBSSID)) {
1242c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    // We found a new candidate that we are going to connect to, then
1243c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    // increase its connection count
1244c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    mWifiConnectionStatistics.incrementOrAddUntrusted(untrustedCandidate.SSID, 1, 0);
1245c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    // Remember which SSID we are connecting to
1246c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                    lastUntrustedBSSID = untrustedCandidate.SSID;
1247c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                }
1248c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            }
1249c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            // Now we don't have VPN, and thus don't actually connect to the untrusted candidate
1250c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle            untrustedCandidate = null;
1251c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle        }
1252b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle
1253931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        /**
1254931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle         *  If candidate is found, check the state of the connection so as
1255931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle         *  to decide if we should be acting on this candidate and switching over
1256931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle         */
1257b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        int networkDelta = compareNetwork(candidate);
1258b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle        if (DBG && candidate != null) {
1259b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle            logDbg("attemptAutoJoin compare SSID candidate : delta="
1260b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle                    + Integer.toString(networkDelta) + " "
1261b57df70bdf17ba45ef4d18b11414cb24dcbe1fb9vandwalle                    + candidate.configKey()
1262c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    + " linked=" + (currentConfiguration != null
1263c6f06c628ee3583b60ff31a7da442e0ac7b26d97vandwalle                    && currentConfiguration.isLinked(candidate)));
1264b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        }
12654dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
1266931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle        /**
1267931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle         * Ask WifiStateMachine permission to switch :
1268931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle         * if user is currently streaming voice traffic,
1269931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle         * then we should not be allowed to switch regardless of the delta
1270931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle         */
1271b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        if (mWifiStateMachine.shouldSwitchNetwork(networkDelta)) {
1272b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            if (mStaStaSupported) {
1273b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                logDbg("mStaStaSupported --> error do nothing now ");
1274b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            } else {
1275b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                if (currentConfiguration != null && currentConfiguration.isLinked(candidate)) {
1276b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                    networkSwitchType = AUTO_JOIN_EXTENDED_ROAMING;
1277b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                } else {
1278b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                    networkSwitchType = AUTO_JOIN_OUT_OF_NETWORK_ROAMING;
1279b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                }
1280b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                if (DBG) {
1281b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                    logDbg("AutoJoin auto connect with netId "
1282b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                            + Integer.toString(candidate.networkId)
1283b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                            + " to " + candidate.configKey());
1284b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                }
12852451dbcc4f9641df188326215b204b798eb70c46vandwalle                if (didOverride) {
12862451dbcc4f9641df188326215b204b798eb70c46vandwalle                    candidate.numScorerOverrideAndSwitchedNetwork++;
12872451dbcc4f9641df188326215b204b798eb70c46vandwalle                }
1288c298087de50ea56c31a4ade7ee1e83b313bb63c7vandwalle                candidate.numAssociation++;
1289b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_CONNECT,
1290b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                        candidate.networkId, networkSwitchType, candidate);
12914dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle            }
1292b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        }
12934dc6f3a322806b25d50039614cde1b94fe91ab17vandwalle
1294b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        if (networkSwitchType == AUTO_JOIN_IDLE) {
1295931338d1533d1bd11ba0e5aebb4e4b7b2c8ab056vandwalle            // Attempt same WifiConfiguration roaming
1296b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            ScanResult roamCandidate = attemptRoam(currentConfiguration, 3000);
1297b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle            if (roamCandidate != null) {
1298b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                if (DBG) {
1299b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                    logDbg("AutoJoin auto roam with netId "
1300b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                            + Integer.toString(currentConfiguration.networkId)
1301b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                            + " " + currentConfiguration.configKey() + " to BSSID="
1302b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                            + roamCandidate.BSSID + " freq=" + roamCandidate.frequency
1303b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                            + " RSSI=" + roamCandidate.frequency);
1304b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                }
1305b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle                networkSwitchType = AUTO_JOIN_ROAMING;
1306b07da189850a4bfa268f8ab9be7867935eb2ecb5vandwalle                mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_ROAM,
1307b07da189850a4bfa268f8ab9be7867935eb2ecb5vandwalle                            currentConfiguration.networkId, 1, roamCandidate);
1308f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle            }
1309f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle        }
1310b97e66604f472f67c233bb8f8d9630bb36131e2cvandwalle        if (VDBG) logDbg("Done attemptAutoJoin status=" + Integer.toString(networkSwitchType));
1311f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle    }
1312f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle}
1313f22d23092ab37286a5ef9d257d5bb32c421d2669vandwalle
1314