• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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