|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko; |
|
7 |
|
8 import org.mozilla.gecko.mozglue.JNITarget; |
|
9 |
|
10 import android.content.BroadcastReceiver; |
|
11 import android.content.Context; |
|
12 import android.content.Intent; |
|
13 import android.content.IntentFilter; |
|
14 import android.net.ConnectivityManager; |
|
15 import android.net.DhcpInfo; |
|
16 import android.net.NetworkInfo; |
|
17 import android.net.wifi.WifiManager; |
|
18 import android.telephony.TelephonyManager; |
|
19 import android.util.Log; |
|
20 |
|
21 /* |
|
22 * A part of the work of GeckoNetworkManager is to give an general connection |
|
23 * type based on the current connection. According to spec of NetworkInformation |
|
24 * API version 3, connection types include: bluetooth, cellular, ethernet, none, |
|
25 * wifi and other. The objective of providing such general connection is due to |
|
26 * some security concerns. In short, we don't want to expose the information of |
|
27 * exact network type, especially the cellular network type. |
|
28 * |
|
29 * Current connection is firstly obtained from Android's ConnectivityManager, |
|
30 * which is represented by the constant, and then will be mapped into the |
|
31 * connection type defined in Network Information API version 3. |
|
32 */ |
|
33 |
|
34 public class GeckoNetworkManager extends BroadcastReceiver { |
|
35 private static final String LOGTAG = "GeckoNetworkManager"; |
|
36 |
|
37 static private final GeckoNetworkManager sInstance = new GeckoNetworkManager(); |
|
38 |
|
39 // Connection Type defined in Network Information API v3. |
|
40 private enum ConnectionType { |
|
41 CELLULAR(0), |
|
42 BLUETOOTH(1), |
|
43 ETHERNET(2), |
|
44 WIFI(3), |
|
45 OTHER(4), |
|
46 NONE(5); |
|
47 |
|
48 public final int value; |
|
49 |
|
50 private ConnectionType(int value) { |
|
51 this.value = value; |
|
52 } |
|
53 } |
|
54 |
|
55 private enum InfoType { |
|
56 MCC, |
|
57 MNC |
|
58 } |
|
59 |
|
60 private ConnectionType mConnectionType = ConnectionType.NONE; |
|
61 private final IntentFilter mNetworkFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); |
|
62 |
|
63 // Whether the manager should be listening to Network Information changes. |
|
64 private boolean mShouldBeListening = false; |
|
65 |
|
66 // Whether the manager should notify Gecko that a change in Network |
|
67 // Information happened. |
|
68 private boolean mShouldNotify = false; |
|
69 |
|
70 // The application context used for registering receivers, so |
|
71 // we can unregister them again later. |
|
72 private volatile Context mApplicationContext; |
|
73 |
|
74 public static GeckoNetworkManager getInstance() { |
|
75 return sInstance; |
|
76 } |
|
77 |
|
78 @Override |
|
79 public void onReceive(Context aContext, Intent aIntent) { |
|
80 updateConnectionType(); |
|
81 } |
|
82 |
|
83 public void start(final Context context) { |
|
84 // Note that this initialization clause only runs once. |
|
85 mApplicationContext = context.getApplicationContext(); |
|
86 if (mConnectionType == ConnectionType.NONE) { |
|
87 mConnectionType = getConnectionType(); |
|
88 } |
|
89 |
|
90 mShouldBeListening = true; |
|
91 updateConnectionType(); |
|
92 |
|
93 if (mShouldNotify) { |
|
94 startListening(); |
|
95 } |
|
96 } |
|
97 |
|
98 private void startListening() { |
|
99 final Context appContext = mApplicationContext; |
|
100 if (appContext == null) { |
|
101 Log.w(LOGTAG, "Not registering receiver: no context!"); |
|
102 return; |
|
103 } |
|
104 |
|
105 Log.v(LOGTAG, "Registering receiver."); |
|
106 appContext.registerReceiver(this, mNetworkFilter); |
|
107 } |
|
108 |
|
109 public void stop() { |
|
110 mShouldBeListening = false; |
|
111 |
|
112 if (mShouldNotify) { |
|
113 stopListening(); |
|
114 } |
|
115 } |
|
116 |
|
117 private void stopListening() { |
|
118 if (null == mApplicationContext) { |
|
119 return; |
|
120 } |
|
121 |
|
122 mApplicationContext.unregisterReceiver(this); |
|
123 } |
|
124 |
|
125 private int wifiDhcpGatewayAddress() { |
|
126 if (mConnectionType != ConnectionType.WIFI) { |
|
127 return 0; |
|
128 } |
|
129 |
|
130 if (null == mApplicationContext) { |
|
131 return 0; |
|
132 } |
|
133 |
|
134 try { |
|
135 WifiManager mgr = (WifiManager) mApplicationContext.getSystemService(Context.WIFI_SERVICE); |
|
136 DhcpInfo d = mgr.getDhcpInfo(); |
|
137 if (d == null) { |
|
138 return 0; |
|
139 } |
|
140 |
|
141 return d.gateway; |
|
142 |
|
143 } catch (Exception ex) { |
|
144 // getDhcpInfo() is not documented to require any permissions, but on some devices |
|
145 // requires android.permission.ACCESS_WIFI_STATE. Just catch the generic exception |
|
146 // here and returning 0. Not logging because this could be noisy. |
|
147 return 0; |
|
148 } |
|
149 } |
|
150 |
|
151 private void updateConnectionType() { |
|
152 ConnectionType previousConnectionType = mConnectionType; |
|
153 mConnectionType = getConnectionType(); |
|
154 |
|
155 if (mConnectionType == previousConnectionType || !mShouldNotify) { |
|
156 return; |
|
157 } |
|
158 |
|
159 GeckoAppShell.sendEventToGecko(GeckoEvent.createNetworkEvent( |
|
160 mConnectionType.value, |
|
161 mConnectionType == ConnectionType.WIFI, |
|
162 wifiDhcpGatewayAddress())); |
|
163 } |
|
164 |
|
165 public double[] getCurrentInformation() { |
|
166 return new double[] { mConnectionType.value, |
|
167 (mConnectionType == ConnectionType.WIFI) ? 1.0 : 0.0, |
|
168 wifiDhcpGatewayAddress()}; |
|
169 } |
|
170 |
|
171 public void enableNotifications() { |
|
172 // We set mShouldNotify *after* calling updateConnectionType() to make sure we |
|
173 // don't notify an eventual change in mConnectionType. |
|
174 mConnectionType = ConnectionType.NONE; // force a notification |
|
175 updateConnectionType(); |
|
176 mShouldNotify = true; |
|
177 |
|
178 if (mShouldBeListening) { |
|
179 startListening(); |
|
180 } |
|
181 } |
|
182 |
|
183 public void disableNotifications() { |
|
184 mShouldNotify = false; |
|
185 |
|
186 if (mShouldBeListening) { |
|
187 stopListening(); |
|
188 } |
|
189 } |
|
190 |
|
191 private ConnectionType getConnectionType() { |
|
192 final Context appContext = mApplicationContext; |
|
193 |
|
194 if (null == appContext) { |
|
195 return ConnectionType.NONE; |
|
196 } |
|
197 |
|
198 ConnectivityManager cm = (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE); |
|
199 if (cm == null) { |
|
200 Log.e(LOGTAG, "Connectivity service does not exist"); |
|
201 return ConnectionType.NONE; |
|
202 } |
|
203 |
|
204 NetworkInfo ni = null; |
|
205 try { |
|
206 ni = cm.getActiveNetworkInfo(); |
|
207 } catch (SecurityException se) {} // if we don't have the permission, fall through to null check |
|
208 |
|
209 if (ni == null) { |
|
210 return ConnectionType.NONE; |
|
211 } |
|
212 |
|
213 switch (ni.getType()) { |
|
214 case ConnectivityManager.TYPE_BLUETOOTH: |
|
215 return ConnectionType.BLUETOOTH; |
|
216 case ConnectivityManager.TYPE_ETHERNET: |
|
217 return ConnectionType.ETHERNET; |
|
218 case ConnectivityManager.TYPE_MOBILE: |
|
219 case ConnectivityManager.TYPE_WIMAX: |
|
220 return ConnectionType.CELLULAR; |
|
221 case ConnectivityManager.TYPE_WIFI: |
|
222 return ConnectionType.WIFI; |
|
223 default: |
|
224 Log.w(LOGTAG, "Ignoring the current network type."); |
|
225 return ConnectionType.OTHER; |
|
226 } |
|
227 } |
|
228 |
|
229 private static int getNetworkOperator(InfoType type, Context context) { |
|
230 if (null == context) { |
|
231 return -1; |
|
232 } |
|
233 |
|
234 TelephonyManager tel = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); |
|
235 if (tel == null) { |
|
236 Log.e(LOGTAG, "Telephony service does not exist"); |
|
237 return -1; |
|
238 } |
|
239 |
|
240 String networkOperator = tel.getNetworkOperator(); |
|
241 if (networkOperator == null || networkOperator.length() <= 3) { |
|
242 return -1; |
|
243 } |
|
244 if (type == InfoType.MNC) { |
|
245 return Integer.parseInt(networkOperator.substring(3)); |
|
246 } else if (type == InfoType.MCC) { |
|
247 return Integer.parseInt(networkOperator.substring(0, 3)); |
|
248 } |
|
249 |
|
250 return -1; |
|
251 } |
|
252 |
|
253 /** |
|
254 * These are called from JavaScript ctypes. Avoid letting ProGuard delete them. |
|
255 * |
|
256 * Note that these methods must only be called after GeckoAppShell has been |
|
257 * initialized: they depend on access to the context. |
|
258 */ |
|
259 @JNITarget |
|
260 public static int getMCC() { |
|
261 return getNetworkOperator(InfoType.MCC, GeckoAppShell.getContext().getApplicationContext()); |
|
262 } |
|
263 |
|
264 @JNITarget |
|
265 public static int getMNC() { |
|
266 return getNetworkOperator(InfoType.MNC, GeckoAppShell.getContext().getApplicationContext()); |
|
267 } |
|
268 } |