1/* 2 * Copyright (C) 2010 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 17package android.net.wifi; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.net.BaseNetworkStateTracker; 24import android.net.LinkCapabilities; 25import android.net.LinkQualityInfo; 26import android.net.LinkProperties; 27import android.net.NetworkInfo; 28import android.net.NetworkInfo.DetailedState; 29import android.net.SamplingDataTracker; 30import android.net.WifiLinkQualityInfo; 31import android.os.Handler; 32import android.os.Message; 33import android.os.Messenger; 34import android.util.Slog; 35 36import java.util.concurrent.atomic.AtomicBoolean; 37 38/** 39 * Track the state of wifi for connectivity service. 40 * 41 * @hide 42 */ 43public class WifiStateTracker extends BaseNetworkStateTracker { 44 45 private static final String NETWORKTYPE = "WIFI"; 46 private static final String TAG = "WifiStateTracker"; 47 48 private static final boolean LOGV = true; 49 50 private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); 51 private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); 52 private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); 53 54 private NetworkInfo.State mLastState = NetworkInfo.State.UNKNOWN; 55 56 private WifiInfo mWifiInfo; 57 58 /* For sending events to connectivity service handler */ 59 private Handler mCsHandler; 60 private BroadcastReceiver mWifiStateReceiver; 61 private WifiManager mWifiManager; 62 63 private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker(); 64 65 public WifiStateTracker(int netType, String networkName) { 66 mNetworkInfo = new NetworkInfo(netType, 0, networkName, ""); 67 mLinkProperties = new LinkProperties(); 68 mLinkCapabilities = new LinkCapabilities(); 69 70 mNetworkInfo.setIsAvailable(false); 71 setTeardownRequested(false); 72 } 73 74 75 public void setTeardownRequested(boolean isRequested) { 76 mTeardownRequested.set(isRequested); 77 } 78 79 public boolean isTeardownRequested() { 80 return mTeardownRequested.get(); 81 } 82 83 /** 84 * Begin monitoring wifi connectivity 85 */ 86 public void startMonitoring(Context context, Handler target) { 87 mCsHandler = target; 88 mContext = context; 89 90 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 91 IntentFilter filter = new IntentFilter(); 92 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 93 filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION); 94 95 mWifiStateReceiver = new WifiStateReceiver(); 96 mContext.registerReceiver(mWifiStateReceiver, filter); 97 } 98 99 /** 100 * Disable connectivity to a network 101 * TODO: do away with return value after making MobileDataStateTracker async 102 */ 103 public boolean teardown() { 104 mTeardownRequested.set(true); 105 mWifiManager.stopWifi(); 106 return true; 107 } 108 109 /** 110 * Re-enable connectivity to a network after a {@link #teardown()}. 111 */ 112 public boolean reconnect() { 113 mTeardownRequested.set(false); 114 mWifiManager.startWifi(); 115 return true; 116 } 117 118 /** 119 * Captive check is complete, switch to network 120 */ 121 @Override 122 public void captivePortalCheckComplete() { 123 mWifiManager.captivePortalCheckComplete(); 124 } 125 126 @Override 127 public void captivePortalCheckCompleted(boolean isCaptivePortal) { 128 // not implemented 129 } 130 131 /** 132 * Turn the wireless radio off for a network. 133 * @param turnOn {@code true} to turn the radio on, {@code false} 134 */ 135 public boolean setRadio(boolean turnOn) { 136 mWifiManager.setWifiEnabled(turnOn); 137 return true; 138 } 139 140 /** 141 * Wi-Fi is considered available as long as we have a connection to the 142 * supplicant daemon and there is at least one enabled network. If a teardown 143 * was explicitly requested, then Wi-Fi can be restarted with a reconnect 144 * request, so it is considered available. If the driver has been stopped 145 * for any reason other than a teardown request, Wi-Fi is considered 146 * unavailable. 147 * @return {@code true} if Wi-Fi connections are possible 148 */ 149 public boolean isAvailable() { 150 return mNetworkInfo.isAvailable(); 151 } 152 153 @Override 154 public void setUserDataEnable(boolean enabled) { 155 Slog.w(TAG, "ignoring setUserDataEnable(" + enabled + ")"); 156 } 157 158 @Override 159 public void setPolicyDataEnable(boolean enabled) { 160 // ignored 161 } 162 163 /** 164 * Check if private DNS route is set for the network 165 */ 166 public boolean isPrivateDnsRouteSet() { 167 return mPrivateDnsRouteSet.get(); 168 } 169 170 /** 171 * Set a flag indicating private DNS route is set 172 */ 173 public void privateDnsRouteSet(boolean enabled) { 174 mPrivateDnsRouteSet.set(enabled); 175 } 176 177 /** 178 * Fetch NetworkInfo for the network 179 */ 180 @Override 181 public NetworkInfo getNetworkInfo() { 182 return new NetworkInfo(mNetworkInfo); 183 } 184 185 /** 186 * Fetch LinkProperties for the network 187 */ 188 @Override 189 public LinkProperties getLinkProperties() { 190 return new LinkProperties(mLinkProperties); 191 } 192 193 /** 194 * A capability is an Integer/String pair, the capabilities 195 * are defined in the class LinkSocket#Key. 196 * 197 * @return a copy of this connections capabilities, may be empty but never null. 198 */ 199 @Override 200 public LinkCapabilities getLinkCapabilities() { 201 return new LinkCapabilities(mLinkCapabilities); 202 } 203 204 /** 205 * Return link info 206 * @return an object of type WifiLinkQualityInfo 207 */ 208 @Override 209 public LinkQualityInfo getLinkQualityInfo() { 210 if (mNetworkInfo == null) { 211 // no data available yet; just return 212 return null; 213 } 214 215 WifiLinkQualityInfo li = new WifiLinkQualityInfo(); 216 li.setNetworkType(mNetworkInfo.getType()); 217 218 synchronized(mSamplingDataTracker.mSamplingDataLock) { 219 mSamplingDataTracker.setCommonLinkQualityInfoFields(li); 220 li.setTxGood(mSamplingDataTracker.getSampledTxPacketCount()); 221 li.setTxBad(mSamplingDataTracker.getSampledTxPacketErrorCount()); 222 } 223 224 // li.setTheoreticalRxBandwidth(??); 225 // li.setTheoreticalTxBandwidth(??); 226 227 if (mWifiInfo != null) { 228 li.setBssid(mWifiInfo.getBSSID()); 229 230 int rssi = mWifiInfo.getRssi(); 231 li.setRssi(rssi); 232 233 li.setNormalizedSignalStrength(mWifiManager.calculateSignalLevel(rssi, 234 LinkQualityInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE)); 235 } 236 237 return li; 238 } 239 240 /** 241 * Check if default route is set 242 */ 243 public boolean isDefaultRouteSet() { 244 return mDefaultRouteSet.get(); 245 } 246 247 /** 248 * Set a flag indicating default route is set for the network 249 */ 250 public void defaultRouteSet(boolean enabled) { 251 mDefaultRouteSet.set(enabled); 252 } 253 254 /** 255 * Return the system properties name associated with the tcp buffer sizes 256 * for this network. 257 */ 258 public String getTcpBufferSizesPropName() { 259 return "net.tcp.buffersize.wifi"; 260 } 261 262 private class WifiStateReceiver extends BroadcastReceiver { 263 @Override 264 public void onReceive(Context context, Intent intent) { 265 266 if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 267 mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( 268 WifiManager.EXTRA_NETWORK_INFO); 269 270 mLinkProperties = intent.getParcelableExtra( 271 WifiManager.EXTRA_LINK_PROPERTIES); 272 if (mLinkProperties == null) { 273 mLinkProperties = new LinkProperties(); 274 } 275 mLinkCapabilities = intent.getParcelableExtra( 276 WifiManager.EXTRA_LINK_CAPABILITIES); 277 if (mLinkCapabilities == null) { 278 mLinkCapabilities = new LinkCapabilities(); 279 } 280 281 mWifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); 282 // don't want to send redundant state messages 283 // but send portal check detailed state notice 284 NetworkInfo.State state = mNetworkInfo.getState(); 285 if (mLastState == state && 286 mNetworkInfo.getDetailedState() != DetailedState.CAPTIVE_PORTAL_CHECK) { 287 return; 288 } else { 289 mLastState = state; 290 /* lets not sample traffic data across state changes */ 291 mSamplingDataTracker.resetSamplingData(); 292 } 293 294 Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, 295 new NetworkInfo(mNetworkInfo)); 296 msg.sendToTarget(); 297 } else if (intent.getAction().equals(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION)) { 298 mLinkProperties = intent.getParcelableExtra(WifiManager.EXTRA_LINK_PROPERTIES); 299 Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); 300 msg.sendToTarget(); 301 } 302 } 303 } 304 305 public void setDependencyMet(boolean met) { 306 // not supported on this network 307 } 308 309 @Override 310 public void addStackedLink(LinkProperties link) { 311 mLinkProperties.addStackedLink(link); 312 } 313 314 @Override 315 public void removeStackedLink(LinkProperties link) { 316 mLinkProperties.removeStackedLink(link); 317 } 318 319 @Override 320 public void supplyMessenger(Messenger messenger) { 321 // not supported on this network 322 } 323 324 @Override 325 public void startSampling(SamplingDataTracker.SamplingSnapshot s) { 326 mSamplingDataTracker.startSampling(s); 327 } 328 329 @Override 330 public void stopSampling(SamplingDataTracker.SamplingSnapshot s) { 331 mSamplingDataTracker.stopSampling(s); 332 } 333} 334 335