1 /* 2 * Copyright (C) 2017 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.settingslib.wifi; 18 19 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED; 20 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.getMaxNetworkSelectionDisableReason; 21 22 import android.content.Context; 23 import android.net.wifi.ScanResult; 24 import android.net.wifi.WifiConfiguration; 25 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; 26 import android.net.wifi.WifiInfo; 27 import android.os.SystemClock; 28 29 import androidx.annotation.VisibleForTesting; 30 31 import com.android.settingslib.R; 32 33 import java.util.Map; 34 35 public class WifiUtils { 36 37 private static final int INVALID_RSSI = -127; 38 39 static final int[] WIFI_PIE = { 40 com.android.internal.R.drawable.ic_wifi_signal_0, 41 com.android.internal.R.drawable.ic_wifi_signal_1, 42 com.android.internal.R.drawable.ic_wifi_signal_2, 43 com.android.internal.R.drawable.ic_wifi_signal_3, 44 com.android.internal.R.drawable.ic_wifi_signal_4 45 }; 46 47 static final int[] NO_INTERNET_WIFI_PIE = { 48 R.drawable.ic_no_internet_wifi_signal_0, 49 R.drawable.ic_no_internet_wifi_signal_1, 50 R.drawable.ic_no_internet_wifi_signal_2, 51 R.drawable.ic_no_internet_wifi_signal_3, 52 R.drawable.ic_no_internet_wifi_signal_4 53 }; 54 buildLoggingSummary(AccessPoint accessPoint, WifiConfiguration config)55 public static String buildLoggingSummary(AccessPoint accessPoint, WifiConfiguration config) { 56 final StringBuilder summary = new StringBuilder(); 57 final WifiInfo info = accessPoint.getInfo(); 58 // Add RSSI/band information for this config, what was seen up to 6 seconds ago 59 // verbose WiFi Logging is only turned on thru developers settings 60 if (accessPoint.isActive() && info != null) { 61 summary.append(" f=" + Integer.toString(info.getFrequency())); 62 } 63 summary.append(" " + getVisibilityStatus(accessPoint)); 64 if (config != null 65 && (config.getNetworkSelectionStatus().getNetworkSelectionStatus() 66 != NETWORK_SELECTION_ENABLED)) { 67 summary.append(" (" + config.getNetworkSelectionStatus().getNetworkStatusString()); 68 if (config.getNetworkSelectionStatus().getDisableTime() > 0) { 69 long now = System.currentTimeMillis(); 70 long diff = (now - config.getNetworkSelectionStatus().getDisableTime()) / 1000; 71 long sec = diff % 60; //seconds 72 long min = (diff / 60) % 60; //minutes 73 long hour = (min / 60) % 60; //hours 74 summary.append(", "); 75 if (hour > 0) summary.append(Long.toString(hour) + "h "); 76 summary.append(Long.toString(min) + "m "); 77 summary.append(Long.toString(sec) + "s "); 78 } 79 summary.append(")"); 80 } 81 82 if (config != null) { 83 NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 84 for (int reason = 0; reason <= getMaxNetworkSelectionDisableReason(); reason++) { 85 if (networkStatus.getDisableReasonCounter(reason) != 0) { 86 summary.append(" ") 87 .append(NetworkSelectionStatus 88 .getNetworkSelectionDisableReasonString(reason)) 89 .append("=") 90 .append(networkStatus.getDisableReasonCounter(reason)); 91 } 92 } 93 } 94 95 return summary.toString(); 96 } 97 98 /** 99 * Returns the visibility status of the WifiConfiguration. 100 * 101 * @return autojoin debugging information 102 * TODO: use a string formatter 103 * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"] 104 * For instance [-40,5/-30,2] 105 */ 106 @VisibleForTesting getVisibilityStatus(AccessPoint accessPoint)107 static String getVisibilityStatus(AccessPoint accessPoint) { 108 final WifiInfo info = accessPoint.getInfo(); 109 StringBuilder visibility = new StringBuilder(); 110 StringBuilder scans24GHz = new StringBuilder(); 111 StringBuilder scans5GHz = new StringBuilder(); 112 StringBuilder scans60GHz = new StringBuilder(); 113 String bssid = null; 114 115 if (accessPoint.isActive() && info != null) { 116 bssid = info.getBSSID(); 117 if (bssid != null) { 118 visibility.append(" ").append(bssid); 119 } 120 visibility.append(" standard = ").append(info.getWifiStandard()); 121 visibility.append(" rssi=").append(info.getRssi()); 122 visibility.append(" "); 123 visibility.append(" score=").append(info.getScore()); 124 if (accessPoint.getSpeed() != AccessPoint.Speed.NONE) { 125 visibility.append(" speed=").append(accessPoint.getSpeedLabel()); 126 } 127 visibility.append(String.format(" tx=%.1f,", info.getSuccessfulTxPacketsPerSecond())); 128 visibility.append(String.format("%.1f,", info.getRetriedTxPacketsPerSecond())); 129 visibility.append(String.format("%.1f ", info.getLostTxPacketsPerSecond())); 130 visibility.append(String.format("rx=%.1f", info.getSuccessfulRxPacketsPerSecond())); 131 } 132 133 int maxRssi5 = INVALID_RSSI; 134 int maxRssi24 = INVALID_RSSI; 135 int maxRssi60 = INVALID_RSSI; 136 final int maxDisplayedScans = 4; 137 int num5 = 0; // number of scanned BSSID on 5GHz band 138 int num24 = 0; // number of scanned BSSID on 2.4Ghz band 139 int num60 = 0; // number of scanned BSSID on 60Ghz band 140 int numBlockListed = 0; 141 142 // TODO: sort list by RSSI or age 143 long nowMs = SystemClock.elapsedRealtime(); 144 for (ScanResult result : accessPoint.getScanResults()) { 145 if (result == null) { 146 continue; 147 } 148 if (result.frequency >= AccessPoint.LOWER_FREQ_5GHZ 149 && result.frequency <= AccessPoint.HIGHER_FREQ_5GHZ) { 150 // Strictly speaking: [4915, 5825] 151 num5++; 152 153 if (result.level > maxRssi5) { 154 maxRssi5 = result.level; 155 } 156 if (num5 <= maxDisplayedScans) { 157 scans5GHz.append( 158 verboseScanResultSummary(accessPoint, result, bssid, 159 nowMs)); 160 } 161 } else if (result.frequency >= AccessPoint.LOWER_FREQ_24GHZ 162 && result.frequency <= AccessPoint.HIGHER_FREQ_24GHZ) { 163 // Strictly speaking: [2412, 2482] 164 num24++; 165 166 if (result.level > maxRssi24) { 167 maxRssi24 = result.level; 168 } 169 if (num24 <= maxDisplayedScans) { 170 scans24GHz.append( 171 verboseScanResultSummary(accessPoint, result, bssid, 172 nowMs)); 173 } 174 } else if (result.frequency >= AccessPoint.LOWER_FREQ_60GHZ 175 && result.frequency <= AccessPoint.HIGHER_FREQ_60GHZ) { 176 // Strictly speaking: [60000, 61000] 177 num60++; 178 179 if (result.level > maxRssi60) { 180 maxRssi60 = result.level; 181 } 182 if (num60 <= maxDisplayedScans) { 183 scans60GHz.append( 184 verboseScanResultSummary(accessPoint, result, bssid, 185 nowMs)); 186 } 187 } 188 } 189 visibility.append(" ["); 190 if (num24 > 0) { 191 visibility.append("(").append(num24).append(")"); 192 if (num24 > maxDisplayedScans) { 193 visibility.append("max=").append(maxRssi24).append(","); 194 } 195 visibility.append(scans24GHz.toString()); 196 } 197 visibility.append(";"); 198 if (num5 > 0) { 199 visibility.append("(").append(num5).append(")"); 200 if (num5 > maxDisplayedScans) { 201 visibility.append("max=").append(maxRssi5).append(","); 202 } 203 visibility.append(scans5GHz.toString()); 204 } 205 visibility.append(";"); 206 if (num60 > 0) { 207 visibility.append("(").append(num60).append(")"); 208 if (num60 > maxDisplayedScans) { 209 visibility.append("max=").append(maxRssi60).append(","); 210 } 211 visibility.append(scans60GHz.toString()); 212 } 213 if (numBlockListed > 0) { 214 visibility.append("!").append(numBlockListed); 215 } 216 visibility.append("]"); 217 218 return visibility.toString(); 219 } 220 221 @VisibleForTesting verboseScanResultSummary(AccessPoint accessPoint, ScanResult result, String bssid, long nowMs)222 /* package */ static String verboseScanResultSummary(AccessPoint accessPoint, ScanResult result, 223 String bssid, long nowMs) { 224 StringBuilder stringBuilder = new StringBuilder(); 225 stringBuilder.append(" \n{").append(result.BSSID); 226 if (result.BSSID.equals(bssid)) { 227 stringBuilder.append("*"); 228 } 229 stringBuilder.append("=").append(result.frequency); 230 stringBuilder.append(",").append(result.level); 231 int speed = getSpecificApSpeed(result, accessPoint.getScoredNetworkCache()); 232 if (speed != AccessPoint.Speed.NONE) { 233 stringBuilder.append(",") 234 .append(accessPoint.getSpeedLabel(speed)); 235 } 236 int ageSeconds = (int) (nowMs - result.timestamp / 1000) / 1000; 237 stringBuilder.append(",").append(ageSeconds).append("s"); 238 stringBuilder.append("}"); 239 return stringBuilder.toString(); 240 } 241 242 @AccessPoint.Speed getSpecificApSpeed(ScanResult result, Map<String, TimestampedScoredNetwork> scoredNetworkCache)243 private static int getSpecificApSpeed(ScanResult result, 244 Map<String, TimestampedScoredNetwork> scoredNetworkCache) { 245 TimestampedScoredNetwork timedScore = scoredNetworkCache.get(result.BSSID); 246 if (timedScore == null) { 247 return AccessPoint.Speed.NONE; 248 } 249 // For debugging purposes we may want to use mRssi rather than result.level as the average 250 // speed wil be determined by mRssi 251 return timedScore.getScore().calculateBadge(result.level); 252 } 253 getMeteredLabel(Context context, WifiConfiguration config)254 public static String getMeteredLabel(Context context, WifiConfiguration config) { 255 // meteredOverride is whether the user manually set the metered setting or not. 256 // meteredHint is whether the network itself is telling us that it is metered 257 if (config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED 258 || (config.meteredHint && !isMeteredOverridden(config))) { 259 return context.getString(R.string.wifi_metered_label); 260 } 261 return context.getString(R.string.wifi_unmetered_label); 262 } 263 264 /** 265 * Returns the Internet icon resource for a given RSSI level. 266 * 267 * @param level The number of bars to show (0-4) 268 * @param noInternet True if a connected Wi-Fi network cannot access the Internet 269 * @throws IllegalArgumentException if an invalid RSSI level is given. 270 */ getInternetIconResource(int level, boolean noInternet)271 public static int getInternetIconResource(int level, boolean noInternet) { 272 if (level < 0 || level >= WIFI_PIE.length) { 273 throw new IllegalArgumentException("No Wifi icon found for level: " + level); 274 } 275 return noInternet ? NO_INTERNET_WIFI_PIE[level] : WIFI_PIE[level]; 276 } 277 isMeteredOverridden(WifiConfiguration config)278 public static boolean isMeteredOverridden(WifiConfiguration config) { 279 return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE; 280 } 281 } 282