NetworkChangeNotifierAutoDetect.java revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.net;
6
7import android.content.BroadcastReceiver;
8import android.content.Context;
9import android.content.Intent;
10import android.content.IntentFilter;
11import android.net.ConnectivityManager;
12import android.net.wifi.WifiInfo;
13import android.net.wifi.WifiManager;
14import android.telephony.TelephonyManager;
15import android.util.Log;
16
17import org.chromium.base.ActivityStatus;
18
19/**
20 * Used by the NetworkChangeNotifier to listens to platform changes in connectivity.
21 * Note that use of this class requires that the app have the platform
22 * ACCESS_NETWORK_STATE permission.
23 */
24public class NetworkChangeNotifierAutoDetect extends BroadcastReceiver
25        implements ActivityStatus.StateListener {
26
27    /** Queries the ConnectivityManager for information about the current connection. */
28    static class ConnectivityManagerDelegate {
29        private final ConnectivityManager mConnectivityManager;
30
31        ConnectivityManagerDelegate(Context context) {
32            mConnectivityManager =
33                    (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
34        }
35
36        // For testing.
37        ConnectivityManagerDelegate() {
38            // All the methods below should be overridden.
39            mConnectivityManager = null;
40        }
41
42        boolean activeNetworkExists() {
43            return mConnectivityManager.getActiveNetworkInfo() != null;
44        }
45
46        boolean isConnected() {
47            return mConnectivityManager.getActiveNetworkInfo().isConnected();
48        }
49
50        int getNetworkType() {
51            return mConnectivityManager.getActiveNetworkInfo().getType();
52        }
53
54        int getNetworkSubtype() {
55            return mConnectivityManager.getActiveNetworkInfo().getSubtype();
56        }
57    }
58
59    /** Queries the WifiManager for SSID of the current Wifi connection. */
60    static class WifiManagerDelegate {
61        private final WifiManager mWifiManager;
62
63        WifiManagerDelegate(Context context) {
64            mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
65        }
66
67        // For testing.
68        WifiManagerDelegate() {
69            // All the methods below should be overridden.
70            mWifiManager = null;
71        }
72
73        String getWifiSSID() {
74            WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
75            if (wifiInfo == null)
76                return "";
77            String ssid = wifiInfo.getSSID();
78            return ssid == null ? "" : ssid;
79        }
80    }
81
82    private static final String TAG = "NetworkChangeNotifierAutoDetect";
83
84    private final NetworkConnectivityIntentFilter mIntentFilter =
85            new NetworkConnectivityIntentFilter();
86
87    private final Observer mObserver;
88
89    private final Context mContext;
90    private ConnectivityManagerDelegate mConnectivityManagerDelegate;
91    private WifiManagerDelegate mWifiManagerDelegate;
92    private boolean mRegistered;
93    private int mConnectionType;
94    private String mWifiSSID;
95
96    /**
97     * Observer notified on the UI thread whenever a new connection type was detected.
98     */
99    public static interface Observer {
100        public void onConnectionTypeChanged(int newConnectionType);
101    }
102
103    public NetworkChangeNotifierAutoDetect(Observer observer, Context context) {
104        mObserver = observer;
105        mContext = context.getApplicationContext();
106        mConnectivityManagerDelegate = new ConnectivityManagerDelegate(context);
107        mWifiManagerDelegate = new WifiManagerDelegate(context);
108        mConnectionType = getCurrentConnectionType();
109        mWifiSSID = getCurrentWifiSSID();
110        ActivityStatus.registerStateListener(this);
111    }
112
113    /**
114     * Allows overriding the ConnectivityManagerDelegate for tests.
115     */
116    void setConnectivityManagerDelegateForTests(ConnectivityManagerDelegate delegate) {
117        mConnectivityManagerDelegate = delegate;
118    }
119
120    /**
121     * Allows overriding the WifiManagerDelegate for tests.
122     */
123    void setWifiManagerDelegateForTests(WifiManagerDelegate delegate) {
124        mWifiManagerDelegate = delegate;
125    }
126
127    public void destroy() {
128        unregisterReceiver();
129    }
130
131    /**
132     * Register a BroadcastReceiver in the given context.
133     */
134    private void registerReceiver() {
135        if (!mRegistered) {
136            mRegistered = true;
137            mContext.registerReceiver(this, mIntentFilter);
138        }
139    }
140
141    /**
142     * Unregister the BroadcastReceiver in the given context.
143     */
144    private void unregisterReceiver() {
145        if (mRegistered) {
146            mRegistered = false;
147            mContext.unregisterReceiver(this);
148        }
149    }
150
151    public int getCurrentConnectionType() {
152        // Track exactly what type of connection we have.
153        if (!mConnectivityManagerDelegate.activeNetworkExists() ||
154                !mConnectivityManagerDelegate.isConnected()) {
155            return NetworkChangeNotifier.CONNECTION_NONE;
156        }
157
158        switch (mConnectivityManagerDelegate.getNetworkType()) {
159            case ConnectivityManager.TYPE_ETHERNET:
160                return NetworkChangeNotifier.CONNECTION_ETHERNET;
161            case ConnectivityManager.TYPE_WIFI:
162                return NetworkChangeNotifier.CONNECTION_WIFI;
163            case ConnectivityManager.TYPE_WIMAX:
164                return NetworkChangeNotifier.CONNECTION_4G;
165            case ConnectivityManager.TYPE_MOBILE:
166                // Use information from TelephonyManager to classify the connection.
167                switch (mConnectivityManagerDelegate.getNetworkSubtype()) {
168                    case TelephonyManager.NETWORK_TYPE_GPRS:
169                    case TelephonyManager.NETWORK_TYPE_EDGE:
170                    case TelephonyManager.NETWORK_TYPE_CDMA:
171                    case TelephonyManager.NETWORK_TYPE_1xRTT:
172                    case TelephonyManager.NETWORK_TYPE_IDEN:
173                        return NetworkChangeNotifier.CONNECTION_2G;
174                    case TelephonyManager.NETWORK_TYPE_UMTS:
175                    case TelephonyManager.NETWORK_TYPE_EVDO_0:
176                    case TelephonyManager.NETWORK_TYPE_EVDO_A:
177                    case TelephonyManager.NETWORK_TYPE_HSDPA:
178                    case TelephonyManager.NETWORK_TYPE_HSUPA:
179                    case TelephonyManager.NETWORK_TYPE_HSPA:
180                    case TelephonyManager.NETWORK_TYPE_EVDO_B:
181                    case TelephonyManager.NETWORK_TYPE_EHRPD:
182                    case TelephonyManager.NETWORK_TYPE_HSPAP:
183                        return NetworkChangeNotifier.CONNECTION_3G;
184                    case TelephonyManager.NETWORK_TYPE_LTE:
185                        return NetworkChangeNotifier.CONNECTION_4G;
186                    default:
187                        return NetworkChangeNotifier.CONNECTION_UNKNOWN;
188                }
189            default:
190                return NetworkChangeNotifier.CONNECTION_UNKNOWN;
191        }
192    }
193
194    private String getCurrentWifiSSID() {
195        if (getCurrentConnectionType() != NetworkChangeNotifier.CONNECTION_WIFI)
196            return "";
197        return mWifiManagerDelegate.getWifiSSID();
198    }
199
200    // BroadcastReceiver
201    @Override
202    public void onReceive(Context context, Intent intent) {
203        connectionTypeChanged();
204    }
205
206    // ActivityStatus.StateListener
207    @Override
208    public void onActivityStateChange(int state) {
209        if (state == ActivityStatus.RESUMED) {
210            // Note that this also covers the case where the main activity is created. The CREATED
211            // event is always followed by the RESUMED event. This is a temporary "hack" until
212            // http://crbug.com/176837 is fixed. The CREATED event can't be used reliably for now
213            // since its notification is deferred. This means that it can immediately follow a
214            // DESTROYED/STOPPED/... event which is problematic.
215            // TODO(pliard): fix http://crbug.com/176837.
216            connectionTypeChanged();
217            registerReceiver();
218        } else if (state == ActivityStatus.PAUSED) {
219            unregisterReceiver();
220        }
221    }
222
223    private void connectionTypeChanged() {
224        int newConnectionType = getCurrentConnectionType();
225        String newWifiSSID = getCurrentWifiSSID();
226        if (newConnectionType == mConnectionType && newWifiSSID.equals(mWifiSSID))
227            return;
228
229        mConnectionType = newConnectionType;
230        mWifiSSID = newWifiSSID;
231        Log.d(TAG, "Network connectivity changed, type is: " + mConnectionType);
232        mObserver.onConnectionTypeChanged(newConnectionType);
233    }
234
235    private static class NetworkConnectivityIntentFilter extends IntentFilter {
236        NetworkConnectivityIntentFilter() {
237            addAction(ConnectivityManager.CONNECTIVITY_ACTION);
238        }
239    }
240}
241