summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/tethering
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/tethering')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java41
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java117
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java45
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java181
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java25
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tethering/WifiManagerWrapper.java41
6 files changed, 450 insertions, 0 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java
new file mode 100644
index 00000000..369a6cf6
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringBroadcastReceiver.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2020LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package se.leap.bitmaskclient.tethering;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.WifiManager;
+import android.util.Log;
+
+public class TetheringBroadcastReceiver extends BroadcastReceiver {
+ private static final String TAG = TetheringBroadcastReceiver.class.getSimpleName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if ("android.net.wifi.WIFI_AP_STATE_CHANGED".equals(intent.getAction())) {
+ Log.d(TAG, "TETHERING WIFI_AP_STATE_CHANGED");
+ TetheringStateManager.updateWifiTetheringState();
+ } else if ("android.net.conn.TETHER_STATE_CHANGED".equals(intent.getAction())) {
+ Log.d(TAG, "TETHERING TETHER_STATE_CHANGED");
+ TetheringStateManager.updateUsbTetheringState();
+ TetheringStateManager.updateBluetoothTetheringState();
+ TetheringStateManager.updateWifiTetheringState();
+ }
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java
new file mode 100644
index 00000000..75d29417
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringObservable.java
@@ -0,0 +1,117 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.tethering;
+
+import android.support.annotation.NonNull;
+
+import java.util.Observable;
+
+public class TetheringObservable extends Observable {
+ private static TetheringObservable instance;
+
+ private TetheringState tetheringState;
+
+ private TetheringObservable() {
+ tetheringState = new TetheringState();
+ }
+
+ public static TetheringObservable getInstance() {
+ if (instance == null) {
+ instance = new TetheringObservable();
+ }
+ return instance;
+ }
+
+ public static void allowVpnWifiTethering(boolean enabled) {
+ if (getInstance().tetheringState.isVpnWifiTetheringAllowed != enabled) {
+ getInstance().tetheringState.isVpnWifiTetheringAllowed = enabled;
+ getInstance().setChanged();
+ getInstance().notifyObservers();
+ }
+ }
+
+ public static void allowVpnUsbTethering(boolean enabled) {
+ if (getInstance().tetheringState.isVpnUsbTetheringAllowed != enabled) {
+ getInstance().tetheringState.isVpnUsbTetheringAllowed = enabled;
+ getInstance().setChanged();
+ getInstance().notifyObservers();
+ }
+ }
+
+ public static void allowVpnBluetoothTethering(boolean enabled) {
+ if (getInstance().tetheringState.isVpnBluetoothTetheringAllowed != enabled) {
+ getInstance().tetheringState.isVpnBluetoothTetheringAllowed = enabled;
+ getInstance().setChanged();
+ getInstance().notifyObservers();
+ }
+ }
+
+ static void setWifiTethering(boolean enabled, @NonNull String address, @NonNull String interfaceName) {
+ if (getInstance().tetheringState.isWifiTetheringEnabled != enabled ||
+ !getInstance().tetheringState.wifiInterface.equals(interfaceName) ||
+ !getInstance().tetheringState.wifiAddress.equals(address)) {
+ TetheringState state = getInstance().tetheringState;
+ state.isWifiTetheringEnabled = enabled;
+ state.wifiInterface = interfaceName;
+ state.wifiAddress = address;
+ state.lastSeenWifiAddress = address.isEmpty() ? state.lastSeenWifiAddress : address;
+ state.lastSeenWifiInterface = interfaceName.isEmpty() ? state.lastSeenWifiInterface : interfaceName;
+ getInstance().setChanged();
+ getInstance().notifyObservers();
+ }
+
+ }
+
+ static void setUsbTethering(boolean enabled, @NonNull String address, @NonNull String interfaceName) {
+ if (getInstance().tetheringState.isUsbTetheringEnabled != enabled ||
+ !getInstance().tetheringState.usbAddress.equals(address) ||
+ !getInstance().tetheringState.usbInterface.equals(interfaceName)) {
+ TetheringState state = getInstance().tetheringState;
+ state.isUsbTetheringEnabled = enabled;
+ state.usbAddress = address;
+ state.usbInterface = interfaceName;
+ state.lastSeenUsbAddress = address.isEmpty() ? state.lastSeenUsbAddress : address;
+ state.lastSeenUsbInterface = interfaceName.isEmpty() ? state.lastSeenUsbInterface : interfaceName;
+ getInstance().setChanged();
+ getInstance().notifyObservers();
+ }
+ }
+
+ static void setBluetoothTethering(boolean enabled) {
+ if (getInstance().tetheringState.isBluetoothTetheringEnabled != enabled) {
+ getInstance().tetheringState.isBluetoothTetheringEnabled = enabled;
+ getInstance().setChanged();
+ getInstance().notifyObservers();
+ }
+ }
+
+ public boolean isBluetoothTetheringEnabled() {
+ return tetheringState.isBluetoothTetheringEnabled;
+ }
+
+ public boolean isUsbTetheringEnabled() {
+ return tetheringState.isUsbTetheringEnabled;
+ }
+
+ public boolean isWifiTetheringEnabled() {
+ return tetheringState.isWifiTetheringEnabled;
+ }
+
+ public TetheringState getTetheringState() {
+ return tetheringState;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java
new file mode 100644
index 00000000..8ef237c6
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringState.java
@@ -0,0 +1,45 @@
+package se.leap.bitmaskclient.tethering;
+
+public class TetheringState implements Cloneable {
+ public boolean isWifiTetheringEnabled;
+ public boolean isUsbTetheringEnabled;
+ public boolean isBluetoothTetheringEnabled;
+ public boolean isVpnWifiTetheringAllowed;
+ public boolean isVpnUsbTetheringAllowed;
+ public boolean isVpnBluetoothTetheringAllowed;
+ public String wifiInterface = "";
+ public String lastSeenWifiInterface = "";
+ public String wifiAddress = "";
+ public String lastSeenWifiAddress = "";
+ public String usbInterface = "";
+ public String lastSeenUsbInterface = "";
+ public String usbAddress = "";
+ public String lastSeenUsbAddress = "";
+ public String bluetoothInterface = "";
+ public String lastSeenBluetoothInterface = "";
+ public String bluetoothAddress = "";
+ public String lastSeenBluetoothAddress = "";
+
+
+ public boolean tetherWifiVpn() {
+ return isWifiTetheringEnabled && isVpnWifiTetheringAllowed;
+ }
+
+ public boolean tetherUsbVpn() {
+ return isUsbTetheringEnabled && isVpnUsbTetheringAllowed;
+ }
+
+ public boolean tetherBluetoothVpn() {
+ return isBluetoothTetheringEnabled && isVpnBluetoothTetheringAllowed;
+ }
+
+ public boolean hasAnyDeviceTetheringEnabled() {
+ return isBluetoothTetheringEnabled || isUsbTetheringEnabled || isWifiTetheringEnabled;
+ }
+
+ public boolean hasAnyVpnTetheringAllowed() {
+ return isVpnWifiTetheringAllowed || isVpnUsbTetheringAllowed || isVpnBluetoothTetheringAllowed;
+ }
+
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java
new file mode 100644
index 00000000..d3c934f6
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/tethering/TetheringStateManager.java
@@ -0,0 +1,181 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.tethering;
+
+import android.content.Context;
+import android.content.IntentFilter;
+import android.support.annotation.VisibleForTesting;
+
+import java.net.Inet4Address;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.util.Enumeration;
+import java.util.List;
+
+import se.leap.bitmaskclient.utils.Cmd;
+
+import static se.leap.bitmaskclient.utils.PreferenceHelper.isBluetoothTetheringAllowed;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.isUsbTetheringAllowed;
+import static se.leap.bitmaskclient.utils.PreferenceHelper.isWifiTetheringAllowed;
+
+/**
+ * This manager tries to figure out the current tethering states for Wifi, USB and Bluetooth
+ * The default behavior differs for failing attempts to get these states:
+ * Wifi: keeps old state
+ * USB: defaults to false
+ * Bluetooth defaults to false
+ * For Wifi there's a second method to check the current state (see TetheringBroadcastReceiver).
+ * Either of both methods can change the state if they succeed, but are ignored if they fail.
+ * This should avoid any interference between both methods.
+ */
+public class TetheringStateManager {
+ private static final String TAG = TetheringStateManager.class.getSimpleName();
+ private static TetheringStateManager instance;
+
+ private WifiManagerWrapper wifiManager;
+
+ private TetheringStateManager() { }
+
+ public static TetheringStateManager getInstance() {
+ if (instance == null) {
+ instance = new TetheringStateManager();
+ }
+ return instance;
+ }
+
+ public void init(Context context) {
+ TetheringBroadcastReceiver broadcastReceiver = new TetheringBroadcastReceiver();
+ IntentFilter intentFilter = new IntentFilter("android.net.conn.TETHER_STATE_CHANGED");
+ intentFilter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED");
+ context.getApplicationContext().registerReceiver(broadcastReceiver, intentFilter);
+ instance.wifiManager = new WifiManagerWrapper(context);
+ TetheringObservable.allowVpnWifiTethering(isWifiTetheringAllowed(context));
+ TetheringObservable.allowVpnUsbTethering(isUsbTetheringAllowed(context));
+ TetheringObservable.allowVpnBluetoothTethering(isBluetoothTetheringAllowed(context));
+ updateWifiTetheringState();
+ updateUsbTetheringState();
+ updateBluetoothTetheringState();
+ }
+
+ static void updateWifiTetheringState() {
+ WifiManagerWrapper manager = getInstance().wifiManager;
+ try {
+ TetheringObservable.setWifiTethering(manager.isWifiAPEnabled(), getWifiAddressRange(), getWlanInterfaceName());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ static void updateUsbTetheringState() {
+ TetheringObservable.setUsbTethering(isUsbTetheringEnabled(), getUsbAddressRange(), getUsbInterfaceName());
+ }
+
+ static void updateBluetoothTetheringState() {
+ //TetheringObservable.setBluetoothTethering(isBluetoothTetheringEnabled());
+ }
+
+ private static String getWifiAddressRange() {
+ String interfaceAddress = getInterfaceAddress(getWlanInterface());
+ return getAddressRange(interfaceAddress);
+ }
+
+ private static String getUsbAddressRange() {
+ String interfaceAddress = getInterfaceAddress(getUsbInterface());
+ return getAddressRange(interfaceAddress);
+ }
+
+ private static String getWlanInterfaceName() {
+ return getInterfaceName(getWlanInterface());
+ }
+
+ private static String getUsbInterfaceName() {
+ return getInterfaceName(getUsbInterface());
+ }
+
+ private static NetworkInterface getWlanInterface() {
+ return getNetworkInterface(new String[]{"wlan", "eth"});
+ }
+
+ private static NetworkInterface getUsbInterface() {
+ return getNetworkInterface(new String[]{"rndis", "usb"});
+ }
+
+ private static boolean isBluetoothTetheringEnabled() {
+ StringBuilder log = new StringBuilder();
+ boolean hasBtPan = false;
+ try {
+ hasBtPan = Cmd.runBlockingCmd(new String[] {"ifconfig bt-pan"}, log) == 0;
+ //Log.d(TAG, "ifconfig result: " + log.toString());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return hasBtPan;
+
+ }
+
+ private static boolean isUsbTetheringEnabled() {
+ return getUsbInterface() != null;
+ }
+
+ private static String getAddressRange(String interfaceAddress) {
+ if (interfaceAddress.split("\\.").length == 4) {
+ String result = interfaceAddress.substring(0, interfaceAddress.lastIndexOf("."));
+ result = result + ".0/24";
+ return result;
+ }
+ return "";
+ }
+
+ private static String getInterfaceAddress(NetworkInterface networkInterface) {
+ if (networkInterface != null) {
+ List<InterfaceAddress> ifaceAddresses = networkInterface.getInterfaceAddresses();
+ for (InterfaceAddress ifaceAddres : ifaceAddresses) {
+ if (ifaceAddres.getAddress() instanceof Inet4Address) {
+ return ifaceAddres.getAddress().getHostAddress();
+ }
+ }
+ }
+ return "";
+ }
+
+ private static String getInterfaceName(NetworkInterface networkInterface) {
+ if (networkInterface != null) {
+ return networkInterface.getName();
+ }
+ return "";
+ }
+
+ private static NetworkInterface getNetworkInterface(String[] interfaceNames) {
+ try {
+ for(Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
+ NetworkInterface networkInterface = en.nextElement();
+ if(!networkInterface.isLoopback()){
+ for (String interfaceName : interfaceNames) {
+ if (networkInterface.getName().contains(interfaceName)) {
+ return networkInterface;
+ }
+ }
+ }
+ }
+ } catch(Exception e){
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java
new file mode 100644
index 00000000..f29a87f8
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiHotspotState.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.tethering;
+
+public enum WifiHotspotState {
+ WIFI_AP_STATE_DISABLING,
+ WIFI_AP_STATE_DISABLED,
+ WIFI_AP_STATE_ENABLING,
+ WIFI_AP_STATE_ENABLED,
+ WIFI_AP_STATE_FAILED
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/tethering/WifiManagerWrapper.java b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiManagerWrapper.java
new file mode 100644
index 00000000..ed395d7f
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/tethering/WifiManagerWrapper.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.tethering;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+
+import java.lang.reflect.Method;
+
+/**
+ * This Wrapper allows better Unit testing.
+ */
+class WifiManagerWrapper {
+
+ private WifiManager wifiManager;
+
+ WifiManagerWrapper(Context context) {
+ this.wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ }
+
+ boolean isWifiAPEnabled() throws Exception {
+ Method method = wifiManager.getClass().getMethod("getWifiApState");
+ int tmp = ((Integer) method.invoke(wifiManager));
+ return WifiHotspotState.WIFI_AP_STATE_ENABLED.ordinal() == tmp % 10;
+ }
+
+}