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