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 | |
5 | package org.chromium.net; |
6 | |
7 | import android.content.BroadcastReceiver; |
8 | import android.content.Context; |
9 | import android.content.Intent; |
10 | import android.content.IntentFilter; |
11 | import android.net.ConnectivityManager; |
12 | import android.net.NetworkInfo; |
13 | import android.telephony.TelephonyManager; |
14 | import android.util.Log; |
15 | |
16 | import 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 | */ |
23 | public 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 | } |