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