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 buildLoggingSummary(AccessPoint accessPoint, WifiConfiguration config)39 public static String buildLoggingSummary(AccessPoint accessPoint, WifiConfiguration config) { 40 final StringBuilder summary = new StringBuilder(); 41 final WifiInfo info = accessPoint.getInfo(); 42 // Add RSSI/band information for this config, what was seen up to 6 seconds ago 43 // verbose WiFi Logging is only turned on thru developers settings 44 if (accessPoint.isActive() && info != null) { 45 summary.append(" f=" + Integer.toString(info.getFrequency())); 46 } 47 summary.append(" " + getVisibilityStatus(accessPoint)); 48 if (config != null 49 && (config.getNetworkSelectionStatus().getNetworkSelectionStatus() 50 != NETWORK_SELECTION_ENABLED)) { 51 summary.append(" (" + config.getNetworkSelectionStatus().getNetworkStatusString()); 52 if (config.getNetworkSelectionStatus().getDisableTime() > 0) { 53 long now = System.currentTimeMillis(); 54 long diff = (now - config.getNetworkSelectionStatus().getDisableTime()) / 1000; 55 long sec = diff % 60; //seconds 56 long min = (diff / 60) % 60; //minutes 57 long hour = (min / 60) % 60; //hours 58 summary.append(", "); 59 if (hour > 0) summary.append(Long.toString(hour) + "h "); 60 summary.append(Long.toString(min) + "m "); 61 summary.append(Long.toString(sec) + "s "); 62 } 63 summary.append(")"); 64 } 65 66 if (config != null) { 67 NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); 68 for (int reason = 0; reason <= getMaxNetworkSelectionDisableReason(); reason++) { 69 if (networkStatus.getDisableReasonCounter(reason) != 0) { 70 summary.append(" ") 71 .append(NetworkSelectionStatus 72 .getNetworkSelectionDisableReasonString(reason)) 73 .append("=") 74 .append(networkStatus.getDisableReasonCounter(reason)); 75 } 76 } 77 } 78 79 return summary.toString(); 80 } 81 82 /** 83 * Returns the visibility status of the WifiConfiguration. 84 * 85 * @return autojoin debugging information 86 * TODO: use a string formatter 87 * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"] 88 * For instance [-40,5/-30,2] 89 */ 90 @VisibleForTesting getVisibilityStatus(AccessPoint accessPoint)91 static String getVisibilityStatus(AccessPoint accessPoint) { 92 final WifiInfo info = accessPoint.getInfo(); 93 StringBuilder visibility = new StringBuilder(); 94 StringBuilder scans24GHz = new StringBuilder(); 95 StringBuilder scans5GHz = new StringBuilder(); 96 String bssid = null; 97 98 if (accessPoint.isActive() && info != null) { 99 bssid = info.getBSSID(); 100 if (bssid != null) { 101 visibility.append(" ").append(bssid); 102 } 103 visibility.append(" standard = ").append(info.getWifiStandard()); 104 visibility.append(" rssi=").append(info.getRssi()); 105 visibility.append(" "); 106 visibility.append(" score=").append(info.getScore()); 107 if (accessPoint.getSpeed() != AccessPoint.Speed.NONE) { 108 visibility.append(" speed=").append(accessPoint.getSpeedLabel()); 109 } 110 visibility.append(String.format(" tx=%.1f,", info.getSuccessfulTxPacketsPerSecond())); 111 visibility.append(String.format("%.1f,", info.getRetriedTxPacketsPerSecond())); 112 visibility.append(String.format("%.1f ", info.getLostTxPacketsPerSecond())); 113 visibility.append(String.format("rx=%.1f", info.getSuccessfulRxPacketsPerSecond())); 114 } 115 116 int maxRssi5 = INVALID_RSSI; 117 int maxRssi24 = INVALID_RSSI; 118 final int maxDisplayedScans = 4; 119 int num5 = 0; // number of scanned BSSID on 5GHz band 120 int num24 = 0; // number of scanned BSSID on 2.4Ghz band 121 int numBlackListed = 0; 122 123 // TODO: sort list by RSSI or age 124 long nowMs = SystemClock.elapsedRealtime(); 125 for (ScanResult result : accessPoint.getScanResults()) { 126 if (result == null) { 127 continue; 128 } 129 if (result.frequency >= AccessPoint.LOWER_FREQ_5GHZ 130 && result.frequency <= AccessPoint.HIGHER_FREQ_5GHZ) { 131 // Strictly speaking: [4915, 5825] 132 num5++; 133 134 if (result.level > maxRssi5) { 135 maxRssi5 = result.level; 136 } 137 if (num5 <= maxDisplayedScans) { 138 scans5GHz.append( 139 verboseScanResultSummary(accessPoint, result, bssid, 140 nowMs)); 141 } 142 } else if (result.frequency >= AccessPoint.LOWER_FREQ_24GHZ 143 && result.frequency <= AccessPoint.HIGHER_FREQ_24GHZ) { 144 // Strictly speaking: [2412, 2482] 145 num24++; 146 147 if (result.level > maxRssi24) { 148 maxRssi24 = result.level; 149 } 150 if (num24 <= maxDisplayedScans) { 151 scans24GHz.append( 152 verboseScanResultSummary(accessPoint, result, bssid, 153 nowMs)); 154 } 155 } 156 } 157 visibility.append(" ["); 158 if (num24 > 0) { 159 visibility.append("(").append(num24).append(")"); 160 if (num24 > maxDisplayedScans) { 161 visibility.append("max=").append(maxRssi24).append(","); 162 } 163 visibility.append(scans24GHz.toString()); 164 } 165 visibility.append(";"); 166 if (num5 > 0) { 167 visibility.append("(").append(num5).append(")"); 168 if (num5 > maxDisplayedScans) { 169 visibility.append("max=").append(maxRssi5).append(","); 170 } 171 visibility.append(scans5GHz.toString()); 172 } 173 if (numBlackListed > 0) { 174 visibility.append("!").append(numBlackListed); 175 } 176 visibility.append("]"); 177 178 return visibility.toString(); 179 } 180 181 @VisibleForTesting verboseScanResultSummary(AccessPoint accessPoint, ScanResult result, String bssid, long nowMs)182 /* package */ static String verboseScanResultSummary(AccessPoint accessPoint, ScanResult result, 183 String bssid, long nowMs) { 184 StringBuilder stringBuilder = new StringBuilder(); 185 stringBuilder.append(" \n{").append(result.BSSID); 186 if (result.BSSID.equals(bssid)) { 187 stringBuilder.append("*"); 188 } 189 stringBuilder.append("=").append(result.frequency); 190 stringBuilder.append(",").append(result.level); 191 int speed = getSpecificApSpeed(result, accessPoint.getScoredNetworkCache()); 192 if (speed != AccessPoint.Speed.NONE) { 193 stringBuilder.append(",") 194 .append(accessPoint.getSpeedLabel(speed)); 195 } 196 int ageSeconds = (int) (nowMs - result.timestamp / 1000) / 1000; 197 stringBuilder.append(",").append(ageSeconds).append("s"); 198 stringBuilder.append("}"); 199 return stringBuilder.toString(); 200 } 201 202 @AccessPoint.Speed getSpecificApSpeed(ScanResult result, Map<String, TimestampedScoredNetwork> scoredNetworkCache)203 private static int getSpecificApSpeed(ScanResult result, 204 Map<String, TimestampedScoredNetwork> scoredNetworkCache) { 205 TimestampedScoredNetwork timedScore = scoredNetworkCache.get(result.BSSID); 206 if (timedScore == null) { 207 return AccessPoint.Speed.NONE; 208 } 209 // For debugging purposes we may want to use mRssi rather than result.level as the average 210 // speed wil be determined by mRssi 211 return timedScore.getScore().calculateBadge(result.level); 212 } 213 getMeteredLabel(Context context, WifiConfiguration config)214 public static String getMeteredLabel(Context context, WifiConfiguration config) { 215 // meteredOverride is whether the user manually set the metered setting or not. 216 // meteredHint is whether the network itself is telling us that it is metered 217 if (config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED 218 || (config.meteredHint && !isMeteredOverridden(config))) { 219 return context.getString(R.string.wifi_metered_label); 220 } 221 return context.getString(R.string.wifi_unmetered_label); 222 } 223 isMeteredOverridden(WifiConfiguration config)224 public static boolean isMeteredOverridden(WifiConfiguration config) { 225 return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE; 226 } 227 } 228