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