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 17 package com.android.server.wifi; 18 19 import android.Manifest.permission; 20 import android.content.Context; 21 import android.net.INetworkScoreCache; 22 import android.net.NetworkKey; 23 import android.net.ScoredNetwork; 24 import android.net.wifi.ScanResult; 25 import android.net.wifi.WifiManager; 26 import android.util.Log; 27 28 import java.io.FileDescriptor; 29 import java.io.PrintWriter; 30 import java.util.HashMap; 31 import java.util.List; 32 import java.util.Map; 33 34 public class WifiNetworkScoreCache extends INetworkScoreCache.Stub { 35 private static final String TAG = "WifiNetworkScoreCache"; 36 private static final boolean DBG = false; 37 38 // A Network scorer returns a score in the range [-128, +127] 39 // We treat the lowest possible score as though there were no score, effectively allowing the 40 // scorer to provide an RSSI threshold below which a network should not be used. 41 public static final int INVALID_NETWORK_SCORE = Byte.MIN_VALUE; 42 private final Context mContext; 43 44 // The key is of the form "<ssid>"<bssid> 45 // TODO: What about SSIDs that can't be encoded as UTF-8? 46 private final Map<String, ScoredNetwork> mNetworkCache; 47 WifiNetworkScoreCache(Context context)48 public WifiNetworkScoreCache(Context context) { 49 mContext = context; 50 mNetworkCache = new HashMap<String, ScoredNetwork>(); 51 } 52 updateScores(List<android.net.ScoredNetwork> networks)53 @Override public final void updateScores(List<android.net.ScoredNetwork> networks) { 54 if (networks == null) { 55 return; 56 } 57 Log.e(TAG, "updateScores list size=" + networks.size()); 58 59 synchronized(mNetworkCache) { 60 for (ScoredNetwork network : networks) { 61 String networkKey = buildNetworkKey(network); 62 if (networkKey == null) continue; 63 mNetworkCache.put(networkKey, network); 64 } 65 } 66 } 67 clearScores()68 @Override public final void clearScores() { 69 synchronized (mNetworkCache) { 70 mNetworkCache.clear(); 71 } 72 } 73 74 /** 75 * Returns whether there is any score info for the given ScanResult. 76 * 77 * This includes null-score info, so it should only be used when determining whether to request 78 * scores from the network scorer. 79 */ isScoredNetwork(ScanResult result)80 public boolean isScoredNetwork(ScanResult result) { 81 return getScoredNetwork(result) != null; 82 } 83 84 /** 85 * Returns whether there is a non-null score curve for the given ScanResult. 86 * 87 * A null score curve has special meaning - we should never connect to an ephemeral network if 88 * the score curve is null. 89 */ hasScoreCurve(ScanResult result)90 public boolean hasScoreCurve(ScanResult result) { 91 ScoredNetwork network = getScoredNetwork(result); 92 return network != null && network.rssiCurve != null; 93 } 94 getNetworkScore(ScanResult result)95 public int getNetworkScore(ScanResult result) { 96 97 int score = INVALID_NETWORK_SCORE; 98 99 ScoredNetwork network = getScoredNetwork(result); 100 if (network != null && network.rssiCurve != null) { 101 score = network.rssiCurve.lookupScore(result.level); 102 if (DBG) { 103 Log.e(TAG, "getNetworkScore found scored network " + network.networkKey 104 + " score " + Integer.toString(score) 105 + " RSSI " + result.level); 106 } 107 } 108 return score; 109 } 110 111 /** 112 * Returns the ScoredNetwork metered hint for a given ScanResult. 113 * 114 * If there is no ScoredNetwork associated with the ScanResult then false will be returned. 115 */ getMeteredHint(ScanResult result)116 public boolean getMeteredHint(ScanResult result) { 117 ScoredNetwork network = getScoredNetwork(result); 118 return network != null && network.meteredHint; 119 } 120 getNetworkScore(ScanResult result, boolean isActiveNetwork)121 public int getNetworkScore(ScanResult result, boolean isActiveNetwork) { 122 123 int score = INVALID_NETWORK_SCORE; 124 125 ScoredNetwork network = getScoredNetwork(result); 126 if (network != null && network.rssiCurve != null) { 127 score = network.rssiCurve.lookupScore(result.level, isActiveNetwork); 128 if (DBG) { 129 Log.e(TAG, "getNetworkScore found scored network " + network.networkKey 130 + " score " + Integer.toString(score) 131 + " RSSI " + result.level 132 + " isActiveNetwork " + isActiveNetwork); 133 } 134 } 135 return score; 136 } 137 getScoredNetwork(ScanResult result)138 private ScoredNetwork getScoredNetwork(ScanResult result) { 139 String key = buildNetworkKey(result); 140 if (key == null) return null; 141 142 //find it 143 synchronized(mNetworkCache) { 144 ScoredNetwork network = mNetworkCache.get(key); 145 return network; 146 } 147 } 148 buildNetworkKey(ScoredNetwork network)149 private String buildNetworkKey(ScoredNetwork network) { 150 if (network == null || network.networkKey == null) return null; 151 if (network.networkKey.wifiKey == null) return null; 152 if (network.networkKey.type == NetworkKey.TYPE_WIFI) { 153 String key = network.networkKey.wifiKey.ssid; 154 if (key == null) return null; 155 if (network.networkKey.wifiKey.bssid != null) { 156 key = key + network.networkKey.wifiKey.bssid; 157 } 158 return key; 159 } 160 return null; 161 } 162 buildNetworkKey(ScanResult result)163 private String buildNetworkKey(ScanResult result) { 164 if (result == null || result.SSID == null) { 165 return null; 166 } 167 StringBuilder key = new StringBuilder("\""); 168 key.append(result.SSID); 169 key.append("\""); 170 if (result.BSSID != null) { 171 key.append(result.BSSID); 172 } 173 return key.toString(); 174 } 175 dump(FileDescriptor fd, PrintWriter writer, String[] args)176 @Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 177 mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG); 178 writer.println("WifiNetworkScoreCache"); 179 writer.println(" All score curves:"); 180 for (Map.Entry<String, ScoredNetwork> entry : mNetworkCache.entrySet()) { 181 ScoredNetwork scoredNetwork = entry.getValue(); 182 writer.println(" " + entry.getKey() + ": " + scoredNetwork.rssiCurve 183 + ", meteredHint=" + scoredNetwork.meteredHint); 184 } 185 writer.println(" Current network scores:"); 186 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 187 for (ScanResult scanResult : wifiManager.getScanResults()) { 188 writer.println(" " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult)); 189 } 190 } 191 192 } 193