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