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