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.WifiConfiguration; 30 import android.net.wifi.WifiInfo; 31 import android.net.wifi.WifiManager; 32 import android.net.wifi.WifiNetworkScoreCache; 33 import android.os.Handler; 34 import android.os.Looper; 35 import android.provider.Settings; 36 37 import com.android.settingslib.R; 38 39 import java.util.List; 40 41 /** 42 * Track status of Wi-Fi for the Sys UI. 43 */ 44 public class WifiStatusTracker { 45 private final Context mContext; 46 private final WifiNetworkScoreCache mWifiNetworkScoreCache; 47 private final WifiManager mWifiManager; 48 private final NetworkScoreManager mNetworkScoreManager; 49 private final ConnectivityManager mConnectivityManager; 50 private final Handler mHandler = new Handler(Looper.getMainLooper()); 51 private final WifiNetworkScoreCache.CacheListener mCacheListener = 52 new WifiNetworkScoreCache.CacheListener(mHandler) { 53 @Override 54 public void networkCacheUpdated(List<ScoredNetwork> updatedNetworks) { 55 updateStatusLabel(); 56 mCallback.run(); 57 } 58 }; 59 private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder() 60 .clearCapabilities() 61 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) 62 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build(); 63 private final NetworkCallback mNetworkCallback = new NetworkCallback() { 64 // Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable 65 // and onLinkPropertiesChanged. 66 @Override 67 public void onCapabilitiesChanged( 68 Network network, NetworkCapabilities networkCapabilities) { 69 updateStatusLabel(); 70 mCallback.run(); 71 } 72 }; 73 private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() { 74 @Override 75 public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { 76 // network is now the default network, and its capabilities are nc. 77 // This method will always be called immediately after the network becomes the 78 // default, in addition to any time the capabilities change while the network is 79 // the default. 80 mDefaultNetwork = network; 81 mDefaultNetworkCapabilities = nc; 82 updateStatusLabel(); 83 mCallback.run(); 84 } 85 @Override 86 public void onLost(Network network) { 87 // The system no longer has a default network. 88 mDefaultNetwork = null; 89 mDefaultNetworkCapabilities = null; 90 updateStatusLabel(); 91 mCallback.run(); 92 } 93 }; 94 private Network mDefaultNetwork = null; 95 private NetworkCapabilities mDefaultNetworkCapabilities = null; 96 private final Runnable mCallback; 97 98 private WifiInfo mWifiInfo; 99 public boolean enabled; 100 public boolean isCaptivePortal; 101 public boolean isDefaultNetwork; 102 public int state; 103 public boolean connected; 104 public String ssid; 105 public int rssi; 106 public int level; 107 public String statusLabel; 108 WifiStatusTracker(Context context, WifiManager wifiManager, NetworkScoreManager networkScoreManager, ConnectivityManager connectivityManager, Runnable callback)109 public WifiStatusTracker(Context context, WifiManager wifiManager, 110 NetworkScoreManager networkScoreManager, ConnectivityManager connectivityManager, 111 Runnable callback) { 112 mContext = context; 113 mWifiManager = wifiManager; 114 mWifiNetworkScoreCache = new WifiNetworkScoreCache(context); 115 mNetworkScoreManager = networkScoreManager; 116 mConnectivityManager = connectivityManager; 117 mCallback = callback; 118 } 119 setListening(boolean listening)120 public void setListening(boolean listening) { 121 if (listening) { 122 mNetworkScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, 123 mWifiNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK); 124 mWifiNetworkScoreCache.registerListener(mCacheListener); 125 mConnectivityManager.registerNetworkCallback( 126 mNetworkRequest, mNetworkCallback, mHandler); 127 mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler); 128 } else { 129 mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, 130 mWifiNetworkScoreCache); 131 mWifiNetworkScoreCache.unregisterListener(); 132 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 133 mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); 134 } 135 } 136 137 /** 138 * Fetches initial state as if a WifiManager.NETWORK_STATE_CHANGED_ACTION have been received. 139 * This replaces the dependency on the initial sticky broadcast. 140 */ fetchInitialState()141 public void fetchInitialState() { 142 if (mWifiManager == null) { 143 return; 144 } 145 updateWifiState(); 146 final NetworkInfo networkInfo = 147 mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 148 connected = networkInfo != null && networkInfo.isConnected(); 149 mWifiInfo = null; 150 ssid = null; 151 if (connected) { 152 mWifiInfo = mWifiManager.getConnectionInfo(); 153 if (mWifiInfo != null) { 154 if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) { 155 ssid = mWifiInfo.getPasspointProviderFriendlyName(); 156 } else { 157 ssid = getValidSsid(mWifiInfo); 158 } 159 updateRssi(mWifiInfo.getRssi()); 160 maybeRequestNetworkScore(); 161 } 162 } 163 updateStatusLabel(); 164 } 165 handleBroadcast(Intent intent)166 public void handleBroadcast(Intent intent) { 167 if (mWifiManager == null) { 168 return; 169 } 170 String action = intent.getAction(); 171 if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 172 updateWifiState(); 173 } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 174 updateWifiState(); 175 final NetworkInfo networkInfo = 176 intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); 177 connected = networkInfo != null && networkInfo.isConnected(); 178 mWifiInfo = null; 179 ssid = null; 180 if (connected) { 181 mWifiInfo = mWifiManager.getConnectionInfo(); 182 if (mWifiInfo != null) { 183 if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) { 184 ssid = mWifiInfo.getPasspointProviderFriendlyName(); 185 } else { 186 ssid = getValidSsid(mWifiInfo); 187 } 188 updateRssi(mWifiInfo.getRssi()); 189 maybeRequestNetworkScore(); 190 } 191 } 192 updateStatusLabel(); 193 mCallback.run(); 194 } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { 195 // Default to -200 as its below WifiManager.MIN_RSSI. 196 updateRssi(intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)); 197 updateStatusLabel(); 198 mCallback.run(); 199 } 200 } 201 updateWifiState()202 private void updateWifiState() { 203 state = mWifiManager.getWifiState(); 204 enabled = state == WifiManager.WIFI_STATE_ENABLED; 205 } 206 updateRssi(int newRssi)207 private void updateRssi(int newRssi) { 208 rssi = newRssi; 209 level = mWifiManager.calculateSignalLevel(rssi); 210 } 211 maybeRequestNetworkScore()212 private void maybeRequestNetworkScore() { 213 NetworkKey networkKey = NetworkKey.createFromWifiInfo(mWifiInfo); 214 if (mWifiNetworkScoreCache.getScoredNetwork(networkKey) == null) { 215 mNetworkScoreManager.requestScores(new NetworkKey[]{ networkKey }); 216 } 217 } 218 updateStatusLabel()219 private void updateStatusLabel() { 220 NetworkCapabilities networkCapabilities; 221 isDefaultNetwork = false; 222 if (mDefaultNetworkCapabilities != null) { 223 isDefaultNetwork = mDefaultNetworkCapabilities.hasTransport( 224 NetworkCapabilities.TRANSPORT_WIFI); 225 } 226 if (isDefaultNetwork) { 227 // Wifi is connected and the default network. 228 networkCapabilities = mDefaultNetworkCapabilities; 229 } else { 230 networkCapabilities = mConnectivityManager.getNetworkCapabilities( 231 mWifiManager.getCurrentNetwork()); 232 } 233 isCaptivePortal = false; 234 if (networkCapabilities != null) { 235 if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) { 236 statusLabel = mContext.getString(R.string.wifi_status_sign_in_required); 237 isCaptivePortal = true; 238 return; 239 } else if (networkCapabilities.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)) { 240 statusLabel = mContext.getString(R.string.wifi_limited_connection); 241 return; 242 } else if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { 243 final String mode = Settings.Global.getString(mContext.getContentResolver(), 244 Settings.Global.PRIVATE_DNS_MODE); 245 if (networkCapabilities.isPrivateDnsBroken()) { 246 statusLabel = mContext.getString(R.string.private_dns_broken); 247 } else { 248 statusLabel = mContext.getString(R.string.wifi_status_no_internet); 249 } 250 return; 251 } else if (!isDefaultNetwork && mDefaultNetworkCapabilities != null 252 && mDefaultNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { 253 statusLabel = mContext.getString(R.string.wifi_connected_low_quality); 254 return; 255 } 256 } 257 258 ScoredNetwork scoredNetwork = 259 mWifiNetworkScoreCache.getScoredNetwork(NetworkKey.createFromWifiInfo(mWifiInfo)); 260 statusLabel = scoredNetwork == null 261 ? null : AccessPoint.getSpeedLabel(mContext, scoredNetwork, rssi); 262 } 263 264 /** Refresh the status label on Locale changed. */ refreshLocale()265 public void refreshLocale() { 266 updateStatusLabel(); 267 mCallback.run(); 268 } 269 getValidSsid(WifiInfo info)270 private String getValidSsid(WifiInfo info) { 271 String ssid = info.getSSID(); 272 if (ssid != null && !WifiManager.UNKNOWN_SSID.equals(ssid)) { 273 return ssid; 274 } 275 // OK, it's not in the connectionInfo; we have to go hunting for it 276 List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks(); 277 int length = networks.size(); 278 for (int i = 0; i < length; i++) { 279 if (networks.get(i).networkId == info.getNetworkId()) { 280 return networks.get(i).SSID; 281 } 282 } 283 return null; 284 } 285 } 286