• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
5  *
6  *      http://www.apache.org/licenses/LICENSE-2.0
7  *
8  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
9  */
10 
11 package com.android.settingslib.wifi;
12 
13 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
14 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
15 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
16 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
17 
18 import android.content.Context;
19 import android.content.Intent;
20 import android.net.ConnectivityManager;
21 import android.net.ConnectivityManager.NetworkCallback;
22 import android.net.Network;
23 import android.net.NetworkCapabilities;
24 import android.net.NetworkInfo;
25 import android.net.NetworkKey;
26 import android.net.NetworkRequest;
27 import android.net.NetworkScoreManager;
28 import android.net.ScoredNetwork;
29 import android.net.wifi.WifiInfo;
30 import android.net.wifi.WifiManager;
31 import android.net.wifi.WifiNetworkScoreCache;
32 import android.os.Handler;
33 import android.os.Looper;
34 import android.provider.Settings;
35 
36 import com.android.settingslib.R;
37 import com.android.settingslib.Utils;
38 
39 import java.io.PrintWriter;
40 import java.text.SimpleDateFormat;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Set;
44 
45 /**
46  * Track status of Wi-Fi for the Sys UI.
47  */
48 public class WifiStatusTracker {
49     private static final int HISTORY_SIZE = 32;
50     private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
51     private final Context mContext;
52     private final WifiNetworkScoreCache mWifiNetworkScoreCache;
53     private final WifiManager mWifiManager;
54     private final NetworkScoreManager mNetworkScoreManager;
55     private final ConnectivityManager mConnectivityManager;
56     private final Handler mHandler = new Handler(Looper.getMainLooper());
57     private final Set<Integer> mNetworks = new HashSet<>();
58     // Save the previous HISTORY_SIZE states for logging.
59     private final String[] mHistory = new String[HISTORY_SIZE];
60     // Where to copy the next state into.
61     private int mHistoryIndex;
62     private final WifiNetworkScoreCache.CacheListener mCacheListener =
63             new WifiNetworkScoreCache.CacheListener(mHandler) {
64                 @Override
65                 public void networkCacheUpdated(List<ScoredNetwork> updatedNetworks) {
66                     updateStatusLabel();
67                     mCallback.run();
68                 }
69             };
70     private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
71             .clearCapabilities()
72             .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
73             .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
74             .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
75     private final NetworkCallback mNetworkCallback =
76             new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
77         // Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable
78         // and onLinkPropertiesChanged.
79         @Override
80         public void onCapabilitiesChanged(
81                 Network network, NetworkCapabilities networkCapabilities) {
82             boolean isVcnOverWifi = false;
83             boolean isWifi = false;
84             WifiInfo wifiInfo = null;
85             if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
86                 wifiInfo = Utils.tryGetWifiInfoForVcn(networkCapabilities);
87                 isVcnOverWifi = (wifiInfo != null);
88             } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
89                 wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
90                 isWifi = true;
91             }
92             // As long as it is a WiFi network, we will log it in the dumpsys for debugging.
93             if (isVcnOverWifi || isWifi) {
94                 String log = new StringBuilder()
95                         .append(SSDF.format(System.currentTimeMillis())).append(",")
96                         .append("onCapabilitiesChanged: ")
97                         .append("network=").append(network).append(",")
98                         .append("networkCapabilities=").append(networkCapabilities)
99                         .toString();
100                 recordLastWifiNetwork(log);
101             }
102             // Ignore the WiFi network if it doesn't contain any valid WifiInfo, or it is not the
103             // primary WiFi.
104             if (wifiInfo == null || !wifiInfo.isPrimary()) {
105                 // Remove the network from the tracking list once it becomes non-primary.
106                 if (mNetworks.contains(network.getNetId())) {
107                     mNetworks.remove(network.getNetId());
108                 }
109                 return;
110             }
111             if (!mNetworks.contains(network.getNetId())) {
112                 mNetworks.add(network.getNetId());
113             }
114             updateWifiInfo(wifiInfo);
115             updateStatusLabel();
116             mCallback.run();
117         }
118 
119         @Override
120         public void onLost(Network network) {
121             String log = new StringBuilder()
122                     .append(SSDF.format(System.currentTimeMillis())).append(",")
123                     .append("onLost: ")
124                     .append("network=").append(network)
125                     .toString();
126             recordLastWifiNetwork(log);
127             if (mNetworks.contains(network.getNetId())) {
128                 mNetworks.remove(network.getNetId());
129                 updateWifiInfo(null);
130                 updateStatusLabel();
131                 mCallback.run();
132             }
133         }
134     };
135     private final NetworkCallback mDefaultNetworkCallback =
136             new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
137         @Override
138         public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
139             // network is now the default network, and its capabilities are nc.
140             // This method will always be called immediately after the network becomes the
141             // default, in addition to any time the capabilities change while the network is
142             // the default.
143             mDefaultNetwork = network;
144             mDefaultNetworkCapabilities = nc;
145             updateStatusLabel();
146             mCallback.run();
147         }
148         @Override
149         public void onLost(Network network) {
150             // The system no longer has a default network.
151             mDefaultNetwork = null;
152             mDefaultNetworkCapabilities = null;
153             updateStatusLabel();
154             mCallback.run();
155         }
156     };
157     private Network mDefaultNetwork = null;
158     private NetworkCapabilities mDefaultNetworkCapabilities = null;
159     private final Runnable mCallback;
160     private final boolean mSupportMergedUi;
161 
162     private WifiInfo mWifiInfo;
163     public boolean enabled;
164     public boolean isCaptivePortal;
165     public boolean isDefaultNetwork;
166     public boolean isCarrierMerged;
167     public int subId;
168     public int state;
169     public boolean connected;
170     public String ssid;
171     public int rssi;
172     public int level;
173     public String statusLabel;
174 
WifiStatusTracker(Context context, WifiManager wifiManager, NetworkScoreManager networkScoreManager, ConnectivityManager connectivityManager, Runnable callback)175     public WifiStatusTracker(Context context, WifiManager wifiManager,
176             NetworkScoreManager networkScoreManager, ConnectivityManager connectivityManager,
177             Runnable callback) {
178         mContext = context;
179         mWifiManager = wifiManager;
180         mWifiNetworkScoreCache = new WifiNetworkScoreCache(context);
181         mNetworkScoreManager = networkScoreManager;
182         mConnectivityManager = connectivityManager;
183         mCallback = callback;
184         mSupportMergedUi = false;
185     }
186 
setListening(boolean listening)187     public void setListening(boolean listening) {
188         if (listening) {
189             mNetworkScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
190                     mWifiNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK);
191             mWifiNetworkScoreCache.registerListener(mCacheListener);
192             mConnectivityManager.registerNetworkCallback(
193                     mNetworkRequest, mNetworkCallback, mHandler);
194             mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
195         } else {
196             mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI,
197                     mWifiNetworkScoreCache);
198             mWifiNetworkScoreCache.unregisterListener();
199             mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
200             mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback);
201         }
202     }
203 
204     /**
205      * Fetches initial state as if a WifiManager.NETWORK_STATE_CHANGED_ACTION have been received.
206      * This replaces the dependency on the initial sticky broadcast.
207      */
fetchInitialState()208     public void fetchInitialState() {
209         if (mWifiManager == null) {
210             return;
211         }
212         updateWifiState();
213         final NetworkInfo networkInfo =
214                 mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
215         connected = networkInfo != null && networkInfo.isConnected();
216         mWifiInfo = null;
217         ssid = null;
218         if (connected) {
219             mWifiInfo = mWifiManager.getConnectionInfo();
220             if (mWifiInfo != null) {
221                 if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) {
222                     ssid = mWifiInfo.getPasspointProviderFriendlyName();
223                 } else {
224                     ssid = getValidSsid(mWifiInfo);
225                 }
226                 if (mSupportMergedUi) {
227                     isCarrierMerged = mWifiInfo.isCarrierMerged();
228                     subId = mWifiInfo.getSubscriptionId();
229                 }
230                 updateRssi(mWifiInfo.getRssi());
231                 maybeRequestNetworkScore();
232             }
233         }
234         updateStatusLabel();
235     }
236 
handleBroadcast(Intent intent)237     public void handleBroadcast(Intent intent) {
238         if (mWifiManager == null) {
239             return;
240         }
241         String action = intent.getAction();
242         if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
243             updateWifiState();
244         }
245     }
246 
updateWifiInfo(WifiInfo wifiInfo)247     private void updateWifiInfo(WifiInfo wifiInfo) {
248         updateWifiState();
249         connected = wifiInfo != null;
250         mWifiInfo = wifiInfo;
251         ssid = null;
252         if (mWifiInfo != null) {
253             if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) {
254                 ssid = mWifiInfo.getPasspointProviderFriendlyName();
255             } else {
256                 ssid = getValidSsid(mWifiInfo);
257             }
258             if (mSupportMergedUi) {
259                 isCarrierMerged = mWifiInfo.isCarrierMerged();
260                 subId = mWifiInfo.getSubscriptionId();
261             }
262             updateRssi(mWifiInfo.getRssi());
263             maybeRequestNetworkScore();
264         }
265     }
266 
updateWifiState()267     private void updateWifiState() {
268         state = mWifiManager.getWifiState();
269         enabled = state == WifiManager.WIFI_STATE_ENABLED;
270         isCarrierMerged = false;
271         subId = 0;
272     }
273 
updateRssi(int newRssi)274     private void updateRssi(int newRssi) {
275         rssi = newRssi;
276         level = mWifiManager.calculateSignalLevel(rssi);
277     }
278 
maybeRequestNetworkScore()279     private void maybeRequestNetworkScore() {
280         NetworkKey networkKey = NetworkKey.createFromWifiInfo(mWifiInfo);
281         if (mWifiNetworkScoreCache.getScoredNetwork(networkKey) == null) {
282             mNetworkScoreManager.requestScores(new NetworkKey[]{ networkKey });
283         }
284     }
285 
updateStatusLabel()286     private void updateStatusLabel() {
287         if (mWifiManager == null) {
288             return;
289         }
290         NetworkCapabilities networkCapabilities;
291         isDefaultNetwork = false;
292         if (mDefaultNetworkCapabilities != null) {
293             boolean isWifi = mDefaultNetworkCapabilities.hasTransport(
294                     NetworkCapabilities.TRANSPORT_WIFI);
295             boolean isVcnOverWifi = mDefaultNetworkCapabilities.hasTransport(
296                     NetworkCapabilities.TRANSPORT_CELLULAR)
297                             && (Utils.tryGetWifiInfoForVcn(mDefaultNetworkCapabilities) != null);
298             if (isWifi || isVcnOverWifi) {
299                 isDefaultNetwork = true;
300             }
301         }
302         if (isDefaultNetwork) {
303             // Wifi is connected and the default network.
304             networkCapabilities = mDefaultNetworkCapabilities;
305         } else {
306             networkCapabilities = mConnectivityManager.getNetworkCapabilities(
307                     mWifiManager.getCurrentNetwork());
308         }
309         isCaptivePortal = false;
310         if (networkCapabilities != null) {
311             if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
312                 statusLabel = mContext.getString(R.string.wifi_status_sign_in_required);
313                 isCaptivePortal = true;
314                 return;
315             } else if (networkCapabilities.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
316                 statusLabel = mContext.getString(R.string.wifi_limited_connection);
317                 return;
318             } else if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
319                 final String mode = Settings.Global.getString(mContext.getContentResolver(),
320                         Settings.Global.PRIVATE_DNS_MODE);
321                 if (networkCapabilities.isPrivateDnsBroken()) {
322                     statusLabel = mContext.getString(R.string.private_dns_broken);
323                 } else {
324                     statusLabel = mContext.getString(R.string.wifi_status_no_internet);
325                 }
326                 return;
327             } else if (!isDefaultNetwork && mDefaultNetworkCapabilities != null
328                     && mDefaultNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
329                 statusLabel = mContext.getString(R.string.wifi_connected_low_quality);
330                 return;
331             }
332         }
333 
334         ScoredNetwork scoredNetwork =
335                 mWifiNetworkScoreCache.getScoredNetwork(NetworkKey.createFromWifiInfo(mWifiInfo));
336         statusLabel = scoredNetwork == null
337                 ? null : AccessPoint.getSpeedLabel(mContext, scoredNetwork, rssi);
338     }
339 
340     /** Refresh the status label on Locale changed. */
refreshLocale()341     public void refreshLocale() {
342         updateStatusLabel();
343         mCallback.run();
344     }
345 
getValidSsid(WifiInfo info)346     private String getValidSsid(WifiInfo info) {
347         String ssid = info.getSSID();
348         if (ssid != null && !WifiManager.UNKNOWN_SSID.equals(ssid)) {
349             return ssid;
350         }
351         return null;
352     }
353 
recordLastWifiNetwork(String log)354     private void recordLastWifiNetwork(String log) {
355         mHistory[mHistoryIndex] = log;
356         mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
357     }
358 
359     /** Dump function. */
dump(PrintWriter pw)360     public void dump(PrintWriter pw) {
361         pw.println("  - WiFi Network History ------");
362         int size = 0;
363         for (int i = 0; i < HISTORY_SIZE; i++) {
364             if (mHistory[i] != null) size++;
365         }
366         // Print out the previous states in ordered number.
367         for (int i = mHistoryIndex + HISTORY_SIZE - 1;
368                 i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
369             pw.println("  Previous WiFiNetwork("
370                     + (mHistoryIndex + HISTORY_SIZE - i) + "): "
371                     + mHistory[i & (HISTORY_SIZE - 1)]);
372         }
373     }
374 }
375