michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko; michael@0: michael@0: import org.mozilla.gecko.mozglue.JNITarget; michael@0: michael@0: import android.content.BroadcastReceiver; michael@0: import android.content.Context; michael@0: import android.content.Intent; michael@0: import android.content.IntentFilter; michael@0: import android.net.ConnectivityManager; michael@0: import android.net.DhcpInfo; michael@0: import android.net.NetworkInfo; michael@0: import android.net.wifi.WifiManager; michael@0: import android.telephony.TelephonyManager; michael@0: import android.util.Log; michael@0: michael@0: /* michael@0: * A part of the work of GeckoNetworkManager is to give an general connection michael@0: * type based on the current connection. According to spec of NetworkInformation michael@0: * API version 3, connection types include: bluetooth, cellular, ethernet, none, michael@0: * wifi and other. The objective of providing such general connection is due to michael@0: * some security concerns. In short, we don't want to expose the information of michael@0: * exact network type, especially the cellular network type. michael@0: * michael@0: * Current connection is firstly obtained from Android's ConnectivityManager, michael@0: * which is represented by the constant, and then will be mapped into the michael@0: * connection type defined in Network Information API version 3. michael@0: */ michael@0: michael@0: public class GeckoNetworkManager extends BroadcastReceiver { michael@0: private static final String LOGTAG = "GeckoNetworkManager"; michael@0: michael@0: static private final GeckoNetworkManager sInstance = new GeckoNetworkManager(); michael@0: michael@0: // Connection Type defined in Network Information API v3. michael@0: private enum ConnectionType { michael@0: CELLULAR(0), michael@0: BLUETOOTH(1), michael@0: ETHERNET(2), michael@0: WIFI(3), michael@0: OTHER(4), michael@0: NONE(5); michael@0: michael@0: public final int value; michael@0: michael@0: private ConnectionType(int value) { michael@0: this.value = value; michael@0: } michael@0: } michael@0: michael@0: private enum InfoType { michael@0: MCC, michael@0: MNC michael@0: } michael@0: michael@0: private ConnectionType mConnectionType = ConnectionType.NONE; michael@0: private final IntentFilter mNetworkFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); michael@0: michael@0: // Whether the manager should be listening to Network Information changes. michael@0: private boolean mShouldBeListening = false; michael@0: michael@0: // Whether the manager should notify Gecko that a change in Network michael@0: // Information happened. michael@0: private boolean mShouldNotify = false; michael@0: michael@0: // The application context used for registering receivers, so michael@0: // we can unregister them again later. michael@0: private volatile Context mApplicationContext; michael@0: michael@0: public static GeckoNetworkManager getInstance() { michael@0: return sInstance; michael@0: } michael@0: michael@0: @Override michael@0: public void onReceive(Context aContext, Intent aIntent) { michael@0: updateConnectionType(); michael@0: } michael@0: michael@0: public void start(final Context context) { michael@0: // Note that this initialization clause only runs once. michael@0: mApplicationContext = context.getApplicationContext(); michael@0: if (mConnectionType == ConnectionType.NONE) { michael@0: mConnectionType = getConnectionType(); michael@0: } michael@0: michael@0: mShouldBeListening = true; michael@0: updateConnectionType(); michael@0: michael@0: if (mShouldNotify) { michael@0: startListening(); michael@0: } michael@0: } michael@0: michael@0: private void startListening() { michael@0: final Context appContext = mApplicationContext; michael@0: if (appContext == null) { michael@0: Log.w(LOGTAG, "Not registering receiver: no context!"); michael@0: return; michael@0: } michael@0: michael@0: Log.v(LOGTAG, "Registering receiver."); michael@0: appContext.registerReceiver(this, mNetworkFilter); michael@0: } michael@0: michael@0: public void stop() { michael@0: mShouldBeListening = false; michael@0: michael@0: if (mShouldNotify) { michael@0: stopListening(); michael@0: } michael@0: } michael@0: michael@0: private void stopListening() { michael@0: if (null == mApplicationContext) { michael@0: return; michael@0: } michael@0: michael@0: mApplicationContext.unregisterReceiver(this); michael@0: } michael@0: michael@0: private int wifiDhcpGatewayAddress() { michael@0: if (mConnectionType != ConnectionType.WIFI) { michael@0: return 0; michael@0: } michael@0: michael@0: if (null == mApplicationContext) { michael@0: return 0; michael@0: } michael@0: michael@0: try { michael@0: WifiManager mgr = (WifiManager) mApplicationContext.getSystemService(Context.WIFI_SERVICE); michael@0: DhcpInfo d = mgr.getDhcpInfo(); michael@0: if (d == null) { michael@0: return 0; michael@0: } michael@0: michael@0: return d.gateway; michael@0: michael@0: } catch (Exception ex) { michael@0: // getDhcpInfo() is not documented to require any permissions, but on some devices michael@0: // requires android.permission.ACCESS_WIFI_STATE. Just catch the generic exception michael@0: // here and returning 0. Not logging because this could be noisy. michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: private void updateConnectionType() { michael@0: ConnectionType previousConnectionType = mConnectionType; michael@0: mConnectionType = getConnectionType(); michael@0: michael@0: if (mConnectionType == previousConnectionType || !mShouldNotify) { michael@0: return; michael@0: } michael@0: michael@0: GeckoAppShell.sendEventToGecko(GeckoEvent.createNetworkEvent( michael@0: mConnectionType.value, michael@0: mConnectionType == ConnectionType.WIFI, michael@0: wifiDhcpGatewayAddress())); michael@0: } michael@0: michael@0: public double[] getCurrentInformation() { michael@0: return new double[] { mConnectionType.value, michael@0: (mConnectionType == ConnectionType.WIFI) ? 1.0 : 0.0, michael@0: wifiDhcpGatewayAddress()}; michael@0: } michael@0: michael@0: public void enableNotifications() { michael@0: // We set mShouldNotify *after* calling updateConnectionType() to make sure we michael@0: // don't notify an eventual change in mConnectionType. michael@0: mConnectionType = ConnectionType.NONE; // force a notification michael@0: updateConnectionType(); michael@0: mShouldNotify = true; michael@0: michael@0: if (mShouldBeListening) { michael@0: startListening(); michael@0: } michael@0: } michael@0: michael@0: public void disableNotifications() { michael@0: mShouldNotify = false; michael@0: michael@0: if (mShouldBeListening) { michael@0: stopListening(); michael@0: } michael@0: } michael@0: michael@0: private ConnectionType getConnectionType() { michael@0: final Context appContext = mApplicationContext; michael@0: michael@0: if (null == appContext) { michael@0: return ConnectionType.NONE; michael@0: } michael@0: michael@0: ConnectivityManager cm = (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE); michael@0: if (cm == null) { michael@0: Log.e(LOGTAG, "Connectivity service does not exist"); michael@0: return ConnectionType.NONE; michael@0: } michael@0: michael@0: NetworkInfo ni = null; michael@0: try { michael@0: ni = cm.getActiveNetworkInfo(); michael@0: } catch (SecurityException se) {} // if we don't have the permission, fall through to null check michael@0: michael@0: if (ni == null) { michael@0: return ConnectionType.NONE; michael@0: } michael@0: michael@0: switch (ni.getType()) { michael@0: case ConnectivityManager.TYPE_BLUETOOTH: michael@0: return ConnectionType.BLUETOOTH; michael@0: case ConnectivityManager.TYPE_ETHERNET: michael@0: return ConnectionType.ETHERNET; michael@0: case ConnectivityManager.TYPE_MOBILE: michael@0: case ConnectivityManager.TYPE_WIMAX: michael@0: return ConnectionType.CELLULAR; michael@0: case ConnectivityManager.TYPE_WIFI: michael@0: return ConnectionType.WIFI; michael@0: default: michael@0: Log.w(LOGTAG, "Ignoring the current network type."); michael@0: return ConnectionType.OTHER; michael@0: } michael@0: } michael@0: michael@0: private static int getNetworkOperator(InfoType type, Context context) { michael@0: if (null == context) { michael@0: return -1; michael@0: } michael@0: michael@0: TelephonyManager tel = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); michael@0: if (tel == null) { michael@0: Log.e(LOGTAG, "Telephony service does not exist"); michael@0: return -1; michael@0: } michael@0: michael@0: String networkOperator = tel.getNetworkOperator(); michael@0: if (networkOperator == null || networkOperator.length() <= 3) { michael@0: return -1; michael@0: } michael@0: if (type == InfoType.MNC) { michael@0: return Integer.parseInt(networkOperator.substring(3)); michael@0: } else if (type == InfoType.MCC) { michael@0: return Integer.parseInt(networkOperator.substring(0, 3)); michael@0: } michael@0: michael@0: return -1; michael@0: } michael@0: michael@0: /** michael@0: * These are called from JavaScript ctypes. Avoid letting ProGuard delete them. michael@0: * michael@0: * Note that these methods must only be called after GeckoAppShell has been michael@0: * initialized: they depend on access to the context. michael@0: */ michael@0: @JNITarget michael@0: public static int getMCC() { michael@0: return getNetworkOperator(InfoType.MCC, GeckoAppShell.getContext().getApplicationContext()); michael@0: } michael@0: michael@0: @JNITarget michael@0: public static int getMNC() { michael@0: return getNetworkOperator(InfoType.MNC, GeckoAppShell.getContext().getApplicationContext()); michael@0: } michael@0: }