1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/GeckoNetworkManager.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,268 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko; 1.10 + 1.11 +import org.mozilla.gecko.mozglue.JNITarget; 1.12 + 1.13 +import android.content.BroadcastReceiver; 1.14 +import android.content.Context; 1.15 +import android.content.Intent; 1.16 +import android.content.IntentFilter; 1.17 +import android.net.ConnectivityManager; 1.18 +import android.net.DhcpInfo; 1.19 +import android.net.NetworkInfo; 1.20 +import android.net.wifi.WifiManager; 1.21 +import android.telephony.TelephonyManager; 1.22 +import android.util.Log; 1.23 + 1.24 +/* 1.25 + * A part of the work of GeckoNetworkManager is to give an general connection 1.26 + * type based on the current connection. According to spec of NetworkInformation 1.27 + * API version 3, connection types include: bluetooth, cellular, ethernet, none, 1.28 + * wifi and other. The objective of providing such general connection is due to 1.29 + * some security concerns. In short, we don't want to expose the information of 1.30 + * exact network type, especially the cellular network type. 1.31 + * 1.32 + * Current connection is firstly obtained from Android's ConnectivityManager, 1.33 + * which is represented by the constant, and then will be mapped into the 1.34 + * connection type defined in Network Information API version 3. 1.35 + */ 1.36 + 1.37 +public class GeckoNetworkManager extends BroadcastReceiver { 1.38 + private static final String LOGTAG = "GeckoNetworkManager"; 1.39 + 1.40 + static private final GeckoNetworkManager sInstance = new GeckoNetworkManager(); 1.41 + 1.42 + // Connection Type defined in Network Information API v3. 1.43 + private enum ConnectionType { 1.44 + CELLULAR(0), 1.45 + BLUETOOTH(1), 1.46 + ETHERNET(2), 1.47 + WIFI(3), 1.48 + OTHER(4), 1.49 + NONE(5); 1.50 + 1.51 + public final int value; 1.52 + 1.53 + private ConnectionType(int value) { 1.54 + this.value = value; 1.55 + } 1.56 + } 1.57 + 1.58 + private enum InfoType { 1.59 + MCC, 1.60 + MNC 1.61 + } 1.62 + 1.63 + private ConnectionType mConnectionType = ConnectionType.NONE; 1.64 + private final IntentFilter mNetworkFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); 1.65 + 1.66 + // Whether the manager should be listening to Network Information changes. 1.67 + private boolean mShouldBeListening = false; 1.68 + 1.69 + // Whether the manager should notify Gecko that a change in Network 1.70 + // Information happened. 1.71 + private boolean mShouldNotify = false; 1.72 + 1.73 + // The application context used for registering receivers, so 1.74 + // we can unregister them again later. 1.75 + private volatile Context mApplicationContext; 1.76 + 1.77 + public static GeckoNetworkManager getInstance() { 1.78 + return sInstance; 1.79 + } 1.80 + 1.81 + @Override 1.82 + public void onReceive(Context aContext, Intent aIntent) { 1.83 + updateConnectionType(); 1.84 + } 1.85 + 1.86 + public void start(final Context context) { 1.87 + // Note that this initialization clause only runs once. 1.88 + mApplicationContext = context.getApplicationContext(); 1.89 + if (mConnectionType == ConnectionType.NONE) { 1.90 + mConnectionType = getConnectionType(); 1.91 + } 1.92 + 1.93 + mShouldBeListening = true; 1.94 + updateConnectionType(); 1.95 + 1.96 + if (mShouldNotify) { 1.97 + startListening(); 1.98 + } 1.99 + } 1.100 + 1.101 + private void startListening() { 1.102 + final Context appContext = mApplicationContext; 1.103 + if (appContext == null) { 1.104 + Log.w(LOGTAG, "Not registering receiver: no context!"); 1.105 + return; 1.106 + } 1.107 + 1.108 + Log.v(LOGTAG, "Registering receiver."); 1.109 + appContext.registerReceiver(this, mNetworkFilter); 1.110 + } 1.111 + 1.112 + public void stop() { 1.113 + mShouldBeListening = false; 1.114 + 1.115 + if (mShouldNotify) { 1.116 + stopListening(); 1.117 + } 1.118 + } 1.119 + 1.120 + private void stopListening() { 1.121 + if (null == mApplicationContext) { 1.122 + return; 1.123 + } 1.124 + 1.125 + mApplicationContext.unregisterReceiver(this); 1.126 + } 1.127 + 1.128 + private int wifiDhcpGatewayAddress() { 1.129 + if (mConnectionType != ConnectionType.WIFI) { 1.130 + return 0; 1.131 + } 1.132 + 1.133 + if (null == mApplicationContext) { 1.134 + return 0; 1.135 + } 1.136 + 1.137 + try { 1.138 + WifiManager mgr = (WifiManager) mApplicationContext.getSystemService(Context.WIFI_SERVICE); 1.139 + DhcpInfo d = mgr.getDhcpInfo(); 1.140 + if (d == null) { 1.141 + return 0; 1.142 + } 1.143 + 1.144 + return d.gateway; 1.145 + 1.146 + } catch (Exception ex) { 1.147 + // getDhcpInfo() is not documented to require any permissions, but on some devices 1.148 + // requires android.permission.ACCESS_WIFI_STATE. Just catch the generic exception 1.149 + // here and returning 0. Not logging because this could be noisy. 1.150 + return 0; 1.151 + } 1.152 + } 1.153 + 1.154 + private void updateConnectionType() { 1.155 + ConnectionType previousConnectionType = mConnectionType; 1.156 + mConnectionType = getConnectionType(); 1.157 + 1.158 + if (mConnectionType == previousConnectionType || !mShouldNotify) { 1.159 + return; 1.160 + } 1.161 + 1.162 + GeckoAppShell.sendEventToGecko(GeckoEvent.createNetworkEvent( 1.163 + mConnectionType.value, 1.164 + mConnectionType == ConnectionType.WIFI, 1.165 + wifiDhcpGatewayAddress())); 1.166 + } 1.167 + 1.168 + public double[] getCurrentInformation() { 1.169 + return new double[] { mConnectionType.value, 1.170 + (mConnectionType == ConnectionType.WIFI) ? 1.0 : 0.0, 1.171 + wifiDhcpGatewayAddress()}; 1.172 + } 1.173 + 1.174 + public void enableNotifications() { 1.175 + // We set mShouldNotify *after* calling updateConnectionType() to make sure we 1.176 + // don't notify an eventual change in mConnectionType. 1.177 + mConnectionType = ConnectionType.NONE; // force a notification 1.178 + updateConnectionType(); 1.179 + mShouldNotify = true; 1.180 + 1.181 + if (mShouldBeListening) { 1.182 + startListening(); 1.183 + } 1.184 + } 1.185 + 1.186 + public void disableNotifications() { 1.187 + mShouldNotify = false; 1.188 + 1.189 + if (mShouldBeListening) { 1.190 + stopListening(); 1.191 + } 1.192 + } 1.193 + 1.194 + private ConnectionType getConnectionType() { 1.195 + final Context appContext = mApplicationContext; 1.196 + 1.197 + if (null == appContext) { 1.198 + return ConnectionType.NONE; 1.199 + } 1.200 + 1.201 + ConnectivityManager cm = (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE); 1.202 + if (cm == null) { 1.203 + Log.e(LOGTAG, "Connectivity service does not exist"); 1.204 + return ConnectionType.NONE; 1.205 + } 1.206 + 1.207 + NetworkInfo ni = null; 1.208 + try { 1.209 + ni = cm.getActiveNetworkInfo(); 1.210 + } catch (SecurityException se) {} // if we don't have the permission, fall through to null check 1.211 + 1.212 + if (ni == null) { 1.213 + return ConnectionType.NONE; 1.214 + } 1.215 + 1.216 + switch (ni.getType()) { 1.217 + case ConnectivityManager.TYPE_BLUETOOTH: 1.218 + return ConnectionType.BLUETOOTH; 1.219 + case ConnectivityManager.TYPE_ETHERNET: 1.220 + return ConnectionType.ETHERNET; 1.221 + case ConnectivityManager.TYPE_MOBILE: 1.222 + case ConnectivityManager.TYPE_WIMAX: 1.223 + return ConnectionType.CELLULAR; 1.224 + case ConnectivityManager.TYPE_WIFI: 1.225 + return ConnectionType.WIFI; 1.226 + default: 1.227 + Log.w(LOGTAG, "Ignoring the current network type."); 1.228 + return ConnectionType.OTHER; 1.229 + } 1.230 + } 1.231 + 1.232 + private static int getNetworkOperator(InfoType type, Context context) { 1.233 + if (null == context) { 1.234 + return -1; 1.235 + } 1.236 + 1.237 + TelephonyManager tel = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 1.238 + if (tel == null) { 1.239 + Log.e(LOGTAG, "Telephony service does not exist"); 1.240 + return -1; 1.241 + } 1.242 + 1.243 + String networkOperator = tel.getNetworkOperator(); 1.244 + if (networkOperator == null || networkOperator.length() <= 3) { 1.245 + return -1; 1.246 + } 1.247 + if (type == InfoType.MNC) { 1.248 + return Integer.parseInt(networkOperator.substring(3)); 1.249 + } else if (type == InfoType.MCC) { 1.250 + return Integer.parseInt(networkOperator.substring(0, 3)); 1.251 + } 1.252 + 1.253 + return -1; 1.254 + } 1.255 + 1.256 + /** 1.257 + * These are called from JavaScript ctypes. Avoid letting ProGuard delete them. 1.258 + * 1.259 + * Note that these methods must only be called after GeckoAppShell has been 1.260 + * initialized: they depend on access to the context. 1.261 + */ 1.262 + @JNITarget 1.263 + public static int getMCC() { 1.264 + return getNetworkOperator(InfoType.MCC, GeckoAppShell.getContext().getApplicationContext()); 1.265 + } 1.266 + 1.267 + @JNITarget 1.268 + public static int getMNC() { 1.269 + return getNetworkOperator(InfoType.MNC, GeckoAppShell.getContext().getApplicationContext()); 1.270 + } 1.271 +}