Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
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/. */
6 package org.mozilla.gecko;
8 import org.mozilla.gecko.mozglue.JNITarget;
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;
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 */
34 public class GeckoNetworkManager extends BroadcastReceiver {
35 private static final String LOGTAG = "GeckoNetworkManager";
37 static private final GeckoNetworkManager sInstance = new GeckoNetworkManager();
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);
48 public final int value;
50 private ConnectionType(int value) {
51 this.value = value;
52 }
53 }
55 private enum InfoType {
56 MCC,
57 MNC
58 }
60 private ConnectionType mConnectionType = ConnectionType.NONE;
61 private final IntentFilter mNetworkFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
63 // Whether the manager should be listening to Network Information changes.
64 private boolean mShouldBeListening = false;
66 // Whether the manager should notify Gecko that a change in Network
67 // Information happened.
68 private boolean mShouldNotify = false;
70 // The application context used for registering receivers, so
71 // we can unregister them again later.
72 private volatile Context mApplicationContext;
74 public static GeckoNetworkManager getInstance() {
75 return sInstance;
76 }
78 @Override
79 public void onReceive(Context aContext, Intent aIntent) {
80 updateConnectionType();
81 }
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 }
90 mShouldBeListening = true;
91 updateConnectionType();
93 if (mShouldNotify) {
94 startListening();
95 }
96 }
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 }
105 Log.v(LOGTAG, "Registering receiver.");
106 appContext.registerReceiver(this, mNetworkFilter);
107 }
109 public void stop() {
110 mShouldBeListening = false;
112 if (mShouldNotify) {
113 stopListening();
114 }
115 }
117 private void stopListening() {
118 if (null == mApplicationContext) {
119 return;
120 }
122 mApplicationContext.unregisterReceiver(this);
123 }
125 private int wifiDhcpGatewayAddress() {
126 if (mConnectionType != ConnectionType.WIFI) {
127 return 0;
128 }
130 if (null == mApplicationContext) {
131 return 0;
132 }
134 try {
135 WifiManager mgr = (WifiManager) mApplicationContext.getSystemService(Context.WIFI_SERVICE);
136 DhcpInfo d = mgr.getDhcpInfo();
137 if (d == null) {
138 return 0;
139 }
141 return d.gateway;
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 }
151 private void updateConnectionType() {
152 ConnectionType previousConnectionType = mConnectionType;
153 mConnectionType = getConnectionType();
155 if (mConnectionType == previousConnectionType || !mShouldNotify) {
156 return;
157 }
159 GeckoAppShell.sendEventToGecko(GeckoEvent.createNetworkEvent(
160 mConnectionType.value,
161 mConnectionType == ConnectionType.WIFI,
162 wifiDhcpGatewayAddress()));
163 }
165 public double[] getCurrentInformation() {
166 return new double[] { mConnectionType.value,
167 (mConnectionType == ConnectionType.WIFI) ? 1.0 : 0.0,
168 wifiDhcpGatewayAddress()};
169 }
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;
178 if (mShouldBeListening) {
179 startListening();
180 }
181 }
183 public void disableNotifications() {
184 mShouldNotify = false;
186 if (mShouldBeListening) {
187 stopListening();
188 }
189 }
191 private ConnectionType getConnectionType() {
192 final Context appContext = mApplicationContext;
194 if (null == appContext) {
195 return ConnectionType.NONE;
196 }
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 }
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
209 if (ni == null) {
210 return ConnectionType.NONE;
211 }
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 }
229 private static int getNetworkOperator(InfoType type, Context context) {
230 if (null == context) {
231 return -1;
232 }
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 }
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 }
250 return -1;
251 }
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 }
264 @JNITarget
265 public static int getMNC() {
266 return getNetworkOperator(InfoType.MNC, GeckoAppShell.getContext().getApplicationContext());
267 }
268 }