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