mobile/android/base/GeckoNetworkManager.java

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial