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