1 /* 2 * Copyright 2019 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 17 package com.android.server.wifi; 18 19 import android.annotation.NonNull; 20 import android.net.wifi.ScanResult; 21 22 import com.android.server.wifi.WifiCandidates.Candidate; 23 import com.android.server.wifi.WifiCandidates.ScoredCandidate; 24 import com.android.server.wifi.proto.WifiScoreCardProto; 25 import com.android.server.wifi.proto.WifiScoreCardProto.Event; 26 27 import java.util.Collection; 28 29 /** 30 * A candidate scorer that uses the scorecard to influence the choice. 31 */ 32 final class ScoreCardBasedScorer implements WifiCandidates.CandidateScorer { 33 34 /** 35 * This should match WifiNetworkSelector.experimentIdFromIdentifier(getIdentifier()) 36 * when using the default ScoringParams. 37 */ 38 public static final int SCORE_CARD_BASED_SCORER_DEFAULT_EXPID = 42902385; 39 40 private final ScoringParams mScoringParams; 41 42 // config_wifi_framework_RSSI_SCORE_OFFSET 43 public static final int RSSI_SCORE_OFFSET = 85; 44 45 // config_wifi_framework_RSSI_SCORE_SLOPE 46 public static final int RSSI_SCORE_SLOPE_IS_4 = 4; 47 48 // config_wifi_framework_5GHz_preference_boost_factor 49 public static final int BAND_5GHZ_AWARD_IS_40 = 40; 50 51 // config_wifiFramework6ghzPreferenceBoostFactor 52 public static final int BAND_6GHZ_AWARD_IS_40 = 40; 53 54 // config_wifi_framework_SECURITY_AWARD 55 public static final int SECURITY_AWARD_IS_80 = 80; 56 57 // config_wifi_framework_LAST_SELECTION_AWARD 58 public static final int LAST_SELECTION_AWARD_IS_480 = 480; 59 60 // config_wifi_framework_current_network_boost 61 public static final int CURRENT_NETWORK_BOOST_IS_16 = 16; 62 63 // config_wifi_framework_SAME_BSSID_AWARD 64 public static final int SAME_BSSID_AWARD_IS_24 = 24; 65 66 // Only use scorecard id we have data from this many polls 67 public static final int MIN_POLLS_FOR_SIGNIFICANCE = 30; 68 69 // Maximum allowable adjustment of the cutoff rssi (dB) 70 public static final int RSSI_RAIL = 5; 71 72 private static final boolean USE_USER_CONNECT_CHOICE = true; 73 ScoreCardBasedScorer(ScoringParams scoringParams)74 ScoreCardBasedScorer(ScoringParams scoringParams) { 75 mScoringParams = scoringParams; 76 } 77 78 @Override getIdentifier()79 public String getIdentifier() { 80 return "ScoreCardBasedScorer"; 81 } 82 83 /** 84 * Calculates an individual candidate's score. 85 */ scoreCandidate(Candidate candidate)86 private ScoredCandidate scoreCandidate(Candidate candidate) { 87 int rssiSaturationThreshold = mScoringParams.getGoodRssi(candidate.getFrequency()); 88 int rssi = Math.min(candidate.getScanRssi(), rssiSaturationThreshold); 89 int cutoff = estimatedCutoff(candidate); 90 int score = (rssi - cutoff) * RSSI_SCORE_SLOPE_IS_4; 91 92 if (ScanResult.is6GHz(candidate.getFrequency())) { 93 score += BAND_6GHZ_AWARD_IS_40; 94 } else if (ScanResult.is5GHz(candidate.getFrequency())) { 95 score += BAND_5GHZ_AWARD_IS_40; 96 } 97 score += (int) (candidate.getLastSelectionWeight() * LAST_SELECTION_AWARD_IS_480); 98 99 if (candidate.isCurrentNetwork()) { 100 score += CURRENT_NETWORK_BOOST_IS_16 + SAME_BSSID_AWARD_IS_24; 101 } 102 103 if (!candidate.isOpenNetwork()) { 104 score += SECURITY_AWARD_IS_80; 105 } 106 107 // To simulate the old strict priority rule, subtract a penalty based on 108 // which nominator added the candidate. 109 score -= 1000 * candidate.getNominatorId(); 110 111 return new ScoredCandidate(score, 10, 112 USE_USER_CONNECT_CHOICE, candidate); 113 } 114 estimatedCutoff(Candidate candidate)115 private int estimatedCutoff(Candidate candidate) { 116 int cutoff = -RSSI_SCORE_OFFSET; 117 int lowest = cutoff - RSSI_RAIL; 118 int highest = cutoff + RSSI_RAIL; 119 WifiScoreCardProto.Signal signal = candidate.getEventStatistics(Event.SIGNAL_POLL); 120 if (signal == null) return cutoff; 121 if (!signal.hasRssi()) return cutoff; 122 if (signal.getRssi().getCount() > MIN_POLLS_FOR_SIGNIFICANCE) { 123 double mean = signal.getRssi().getSum() / signal.getRssi().getCount(); 124 double mean_square = signal.getRssi().getSumOfSquares() / signal.getRssi().getCount(); 125 double variance = mean_square - mean * mean; 126 double sigma = Math.sqrt(variance); 127 double value = mean - 2.0 * sigma; 128 cutoff = (int) Math.min(Math.max(value, lowest), highest); 129 } 130 return cutoff; 131 } 132 133 @Override scoreCandidates(@onNull Collection<Candidate> candidates)134 public ScoredCandidate scoreCandidates(@NonNull Collection<Candidate> candidates) { 135 ScoredCandidate choice = ScoredCandidate.NONE; 136 for (Candidate candidate : candidates) { 137 ScoredCandidate scoredCandidate = scoreCandidate(candidate); 138 if (scoredCandidate.value > choice.value) { 139 choice = scoredCandidate; 140 } 141 } 142 // Here we just return the highest scored candidate; we could 143 // compute a new score, if desired. 144 return choice; 145 } 146 147 } 148