diff --git a/AndroidManifest.xml b/AndroidManifest.xml
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -21,6 +21,7 @@
+
diff --git a/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java b/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java
--- a/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java
+++ b/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java
@@ -371,13 +371,17 @@
new Thread(new Runnable() {
@Override
public void run() {
-
+ Log.d("KDE/LanLinkProvider", "Broadcasting udp package");
String deviceListPrefs = PreferenceManager.getDefaultSharedPreferences(context).getString(CustomDevicesActivity.KEY_CUSTOM_DEVLIST_PREFERENCE, "");
- ArrayList iplist = new ArrayList<>();
+ ArrayList ipList = new ArrayList<>();
if (!deviceListPrefs.isEmpty()) {
- iplist = CustomDevicesActivity.deserializeIpList(deviceListPrefs);
+ ipList = CustomDevicesActivity.deserializeIpList(deviceListPrefs);
+ }
+ ipList.addAll(NetworkHelper.getNetworkBroadcastDestinations());
+
+ for (String ipstr : ipList) {
+ Log.d("KDE/LanLinkProvider", "|Destination ip: " + ipstr);
}
- iplist.add("255.255.255.255"); //Default: broadcast.
NetworkPackage identity = NetworkPackage.createIdentityPackage(context);
identity.set("tcpPort", MIN_PORT);
@@ -395,7 +399,7 @@
if (bytes != null) {
//Log.e("KDE/LanLinkProvider","Sending packet to "+iplist.size()+" ips");
- for (String ipstr : iplist) {
+ for (String ipstr : ipList) {
try {
InetAddress client = InetAddress.getByName(ipstr);
socket.send(new DatagramPacket(bytes, bytes.length, client, MIN_PORT));
@@ -442,6 +446,7 @@
@Override
public void onNetworkChange() {
+ Log.d("KDE/LanLinkProvider", "Network changed");
broadcastUdpPackage();
}
diff --git a/src/org/kde/kdeconnect/Helpers/NetworkHelper.java b/src/org/kde/kdeconnect/Helpers/NetworkHelper.java
--- a/src/org/kde/kdeconnect/Helpers/NetworkHelper.java
+++ b/src/org/kde/kdeconnect/Helpers/NetworkHelper.java
@@ -4,55 +4,227 @@
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo;
+import android.net.wifi.WifiManager;
+import android.text.TextUtils;
import android.util.Log;
+import java.io.BufferedReader;
import java.io.FileReader;
import java.io.LineNumberReader;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
public class NetworkHelper {
+ public static List getNetworkBroadcastDestinations() {
+ // TODO use a more specific broadcast ip
+ return Collections.singletonList("255.255.255.255");
+ }
+
+ @Deprecated
public static boolean isOnMobileNetwork(Context context) {
+ // Stage 0 - check environment
+ Log.d("isOnMobileNetwork", "check begun", new Throwable("stack trace"));
if (context == null) {
+ Log.d("isOnMobileNetwork", "false (no context)");
return false;
}
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
+ Log.d("isOnMobileNetwork", "false (platform version too old)");
return false; //No good way to know it
}
+ long startTime = System.nanoTime();
+ // Stage 1 - check ConnectivityManager
+ //try {
+ boolean mobileFound = false;
+ boolean nonMobileFound = false;
try {
- boolean mobile = false;
+ Log.d("isOnMobileNetwork", "|checking ConnectivityManager networks");
final ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
Network[] networks = connMgr.getAllNetworks();
+ Log.d("isOnMobileNetwork", "|found " + networks.length + " connection(s)");
for (Network network : networks) {
+ Log.d("isOnMobileNetwork", "||checking network: " + network);
NetworkInfo info = connMgr.getNetworkInfo(network);
if (info == null) {
+ Log.d("isOnMobileNetwork", "||couldn't get network info, skipping");
+ continue;
+ }
+ Log.d("isOnMobileNetwork", "|||network info: " + info);
+ int type = info.getType();
+ Log.d("isOnMobileNetwork", "|||network is of type: " + type);
+ if (info.getType() == ConnectivityManager.TYPE_MOBILE) { // it is a mobile data network
+ if (info.isConnected()) {
+ Log.d("isOnMobileNetwork", "|||found connected mobile data network");
+ mobileFound = true;
+ } else {
+ Log.d("isOnMobileNetwork", "|||ignored mobile data network that isn't connected");
+ }
+ mobileFound = mobileFound || info.isConnected();
+ continue;
+ } // else it'sn't
+ if (info.isConnected()) {
+ Log.d("isOnMobileNetwork", "|||found connected non-mobile network");
+ //return false; //We are connected to at least one non-mobile network
+ nonMobileFound = true;
continue;
+ } else {
+ Log.d("isOnMobileNetwork", "|||ignored non-mobile network that isn't connected");
+ }
+ }
+ Log.d("isOnMobileNetwork", "|ConnectivityManager check resulted in: mobile=" + mobileFound + ", nonmobile=" + nonMobileFound);
+ //Log.d("isOnMobileNetwork", "|checking ConnectivityManager interfaces");
+ //String[] connInterfaces = (String[]) ConnectivityManager.class.getDeclaredMethod("getTetherableIfaces").invoke(connMgr);
+ //Log.d("isOnMobileNetwork", "|found " + connInterfaces.length + " interface(s)");
+ //for (String iface : connInterfaces) {
+ // Log.d("isOnMobileNetwork", "||discovered interface: " + iface);
+ //}
+ } catch (Exception e) {
+ Log.e("isOnMobileNetwork", "Exception checking ConnectivityManager", e);
+ return false;
+ }
+ // Stage 2 - check WifiManager
+ boolean wifiTetherEnabled;
+ try {
+ Log.d("isOnMobileNetwork", "|checking WifiManager");
+ final WifiManager wifiMgr = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ wifiTetherEnabled = (Boolean) WifiManager.class.getDeclaredMethod("isWifiApEnabled").invoke(wifiMgr);
+ Log.d("isOnMobileNetwork", "|WifiManager check resulted in: " + wifiTetherEnabled);
+ if (wifiTetherEnabled) {
+ nonMobileFound = true;
+ }
+ } catch (Exception e) {
+ Log.e("isOnMobileNetwork", "Exception reading WifiManager", e);
+ return false;
+ }
+ // Stage 3 - check NetworkInterface interfaces
+ try {
+ boolean usbTetherEnabled = false;
+ boolean btTetherEnabled = false;
+ boolean wifiEnabled = false;
+ Log.d("isOnMobileNetwork", "|checking NetworkInterface interfaces");
+ List niInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
+ Log.d("isOnMobileNetwork", "|found " + niInterfaces.size() + " interface(s)");
+ //TODO listen for changes
+ for (NetworkInterface iface : niInterfaces) {
+ String name = iface.getName();
+ boolean up = iface.isUp();
+ Log.d("isOnMobileNetwork", "||checking interface: up=" + up + ", name=" + name);
+ List inetaddresses = Collections.list(iface.getInetAddresses());
+ Log.d("isOnMobileNetwork", "|||found " + inetaddresses.size() + " inetaddress(es)");
+ for (InetAddress addr : inetaddresses) {
+ Log.d("isOnMobileNetwork", "||||inetaddress: " + addr);
+ }
+ List interfaceAddresses = iface.getInterfaceAddresses();
+ Log.d("isOnMobileNetwork", "|||found " + inetaddresses.size() + " interfaceaddress(es)");
+ for (InterfaceAddress addr : interfaceAddresses) {
+ Log.d("isOnMobileNetwork", "||||interfaceaddress: " + addr);
+ InetAddress broadcastAddr = addr.getBroadcast();
+ Log.d("isOnMobileNetwork", "|||||interfaceaddress broadcast destination: " + broadcastAddr);
}
- if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
- mobile = info.isConnected();
+ if (!up) {
+ Log.d("isOnMobileNetwork", "|||ignoring down interface");
continue;
}
- //Log.e(info.getTypeName(),""+info.isAvailable());
- if (info.isAvailable()) return false; //We are connected to at least one non-mobile network
- }
- if (mobile) { //We suspect we are on a mobile net
- try {
- //Check the number of network neighbours, on data it should be 0
- LineNumberReader is = new LineNumberReader(new FileReader("/proc/net/arp"));
- is.skip(Long.MAX_VALUE);
- //Log.e("NetworkHelper", "procnetarp has " + is.getLineNumber() + " lines");
- if (is.getLineNumber() > 1) { //The first line are the headers
- return false; //I have neighbours, so this doesn't look like a mobile network
+ if (name.startsWith("rndis")) {
+ Log.d("isOnMobileNetwork", "|||found usb tethering interface. its broadcast ip might be usable!!!");
+ usbTetherEnabled = true;
+ } else if (name.equals("bt-pan")) {
+ Log.d("isOnMobileNetwork", "|||found bluetooth tethering interface. its broadcast ip might be usable!!!");
+ btTetherEnabled = true;
+ } else if (name.startsWith("wlan")) {
+ Log.d("isOnMobileNetwork", "|||found wifi interface");
+ wifiEnabled = true;
+ if (wifiTetherEnabled) {
+ Log.d("isOnMobileNetwork", "||||seems like we are wifi tethering, its broadcast ip might be usable!!!");
+ } else {
+ Log.d("isOnMobileNetwork", "||||seems like we are connected to an wifi ap, its broadcast ip might be usable!!!");
}
- } catch (Exception e) {
- Log.e("NetworkHelper", "Exception reading procnetarp");
- e.printStackTrace();
+ } else {
+ Log.d("isOnMobileNetwork", "|||ignoring unknown interface");
}
}
- } catch(Exception e) {
- e.printStackTrace();
- Log.d("isOnMobileNetwork", "Something went wrong, but this is non-critical.");
+ Log.d("isOnMobileNetwork", "|NetworkInterface check resulted in:" +
+ " usb=" + usbTetherEnabled + ", bluetooth=" + btTetherEnabled
+ + ", wifi=" + wifiEnabled);
+ if (usbTetherEnabled || btTetherEnabled || wifiEnabled) {
+ nonMobileFound = true;
+ }
+ } catch (Exception e) {
+ Log.e("isOnMobileNetwork", "Exception checking NetworkInterface interfaces", e);
+ return false;
+ }
+ // Stage 4 - check /proc/net/arp
+ boolean arpTetherFound = false;
+ //if (mobileFound && !nonMobileFound) { //We suspect we are on a mobile net
+ try {
+ Log.d("isOnMobileNetwork", "|checking arp");
+ //Check the number of network neighbours, on data it should be 0
+ BufferedReader in = new BufferedReader(new FileReader("/proc/net/arp"));
+ String line = in.readLine(); //The first line are the headers
+ if (TextUtils.isEmpty(line)) {
+ throw new AssertionError("Expected non-empty procnetarp headers");
+ }
+ List arpLines = new LinkedList<>();
+ while ((line = in.readLine()) != null) {
+ arpLines.add(line);
+ }
+ Log.d("isOnMobileNetwork", "||procnetarp has " + arpLines.size() + " address lines");
+ if (arpLines.size() > 1) {
+ //return false; //I have neighbours, so this doesn't look like a mobile network
+ arpTetherFound = true;
+ }
+ List arpAddresses = new LinkedList<>();
+ for (String arpLine : arpLines) {
+ String[] arpSections = arpLine.replaceAll("\\s+", "\t").split("\t");
+ if (arpSections.length != 6) {
+ throw new AssertionError("Expected 6 procnetarp columns, got " + arpSections.length);
+ }
+ arpAddresses.add(arpSections[0]);
+ Log.d("isOnMobileNetwork", "|||arp row: " + arpLine);
+ }
+ Log.d("isOnMobileNetwork", "||procnetarp has " + arpAddresses.size() + " addresses");
+ for (String address : arpAddresses) {
+ Log.d("isOnMobileNetwork", "|||arp address [usable!!!]: " + address);
+ }
+ Log.d("isOnMobileNetwork", "|arp check resulted in: " + arpTetherFound);
+ } catch (Exception e) {
+ Log.e("isOnMobileNetwork", "Exception reading procnetarp", e);
+ return false;
+ }
+ if (arpTetherFound) {
+ nonMobileFound = true;
+ }
+ // Finally, return results
+ Log.d("isOnMobileNetwork", (System.nanoTime() - startTime) + " nanos have elapsed");
+ if (!mobileFound) {
+ if (nonMobileFound) {
+ Log.d("isOnMobileNetwork", "false (we are connected to only non-mobile network(s))");
+ return false;
+ } else {
+ Log.d("isOnMobileNetwork", "false (we aren't connected to anything?)");
+ return false;
+ }
+ } else {
+ if (nonMobileFound) {
+ Log.d("isOnMobileNetwork", "false (we are also connected to non-mobile network(s))");
+ return false;
+ } else {
+ Log.d("isOnMobileNetwork", "true (we are connected to only mobile data network(s))");
+ return true;
+ }
}
- return false;
+ //}
+ //} catch(Exception e) {
+ // e.printStackTrace();
+ // Log.d("isOnMobileNetwork", "Something went wrong, but this is non-critical, falling back to false");
+ // return false;
+ //}
}
}