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