From 3e2bc20b05c1283110c33451dafdd854b8043583 Mon Sep 17 00:00:00 2001 From: elluisian Date: Sun, 1 Sep 2024 22:15:45 +0200 Subject: [PATCH] MainService: Now service is stopped if interface goes down I was capable of implementing an all-Java solution. For some reason though, I was not capable of making it work with VPNs (needs more testing, I think). --- .../droidvnc_ng/ListenIfAdapter.java | 48 +++++- .../droidvnc_ng/MainActivity.java | 2 +- .../droidvnc_ng/MainService.java | 14 +- .../droidvnc_ng/NetworkInterfaceTester.java | 141 +++++++++++++----- .../net/christianbeier/droidvnc_ng/Utils.kt | 2 +- 5 files changed, 161 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/net/christianbeier/droidvnc_ng/ListenIfAdapter.java b/app/src/main/java/net/christianbeier/droidvnc_ng/ListenIfAdapter.java index d689c39..4084148 100644 --- a/app/src/main/java/net/christianbeier/droidvnc_ng/ListenIfAdapter.java +++ b/app/src/main/java/net/christianbeier/droidvnc_ng/ListenIfAdapter.java @@ -26,6 +26,9 @@ import android.content.res.Resources; import android.content.Context; +import android.os.Handler; +import android.os.Looper; + import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -43,6 +46,7 @@ private static class ViewHolder { } // Data to be shown with the adapter + private ArrayList dataStr; private ArrayList data; private int dataSize; @@ -52,6 +56,9 @@ private static class ViewHolder { private LayoutInflater mInflater; + // UI related + private Handler handler; + public ListenIfAdapter(NetworkInterfaceTester nit, Context context) { @@ -60,8 +67,10 @@ public ListenIfAdapter(NetworkInterfaceTester nit, Context context) { this.mContext = context; this.mInflater = LayoutInflater.from(this.mContext); + this.handler = new Handler(Looper.getMainLooper()); + nit.addOnNetworkStateChangedListener(this); - this.onNetworkStateChanged(nit, null, false); + this.onNetworkStateChanged(nit); } @@ -80,9 +89,19 @@ public int getItemPositionByIfName(String ifName) { + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + return this.handleViewRecreation(position, convertView, parent); + } + @Override public View getView(int position, View convertView, ViewGroup parent) { + return this.handleViewRecreation(position, convertView, parent); + } + + + private View handleViewRecreation(int position, View convertView, ViewGroup parent) { if (convertView == null) { // Check if view must be recreated using the famous ViewHolder pattern convertView = this.mInflater.inflate(R.layout.spinner_row, parent, false); @@ -92,13 +111,14 @@ public View getView(int position, View convertView, ViewGroup parent) { } ViewHolder vh = (ViewHolder)convertView.getTag(); - NetworkInterfaceTester.NetIfData nid = this.getItem(position); - vh.txtLabel.setText(nid.toString()); + String label = this.dataStr.get(position); + vh.txtLabel.setText(label); return convertView; } + @Override public NetworkInterfaceTester.NetIfData getItem(int position) { if (0 <= position && position < this.getCount()) { @@ -108,6 +128,7 @@ public NetworkInterfaceTester.NetIfData getItem(int position) { } + @Override public int getCount() { return this.dataSize; @@ -115,8 +136,27 @@ public int getCount() { - public void onNetworkStateChanged(NetworkInterfaceTester nit, NetworkInterface iface, boolean enabled) { + public void onNetworkStateChanged(NetworkInterfaceTester nit) { this.data = nit.getAvailableInterfaces(); this.dataSize = this.data.size(); + + this.dataStr = new ArrayList<>(); + for (NetworkInterfaceTester.NetIfData nid : this.data) { + String actualName = " (" + nid.getName() + ")"; + String displayName = nid.getDisplayName(); + + if (nid.getName().equals("0.0.0.0")) { + displayName = this.mContext.getResources().getString(R.string.main_activity_settings_listenif_spin_any); + } + + this.dataStr.add(displayName + actualName); + } + + // update Spinner + this.handler.post(new Runnable() { + public void run() { + ListenIfAdapter.this.notifyDataSetChanged(); + } + }); } } \ No newline at end of file diff --git a/app/src/main/java/net/christianbeier/droidvnc_ng/MainActivity.java b/app/src/main/java/net/christianbeier/droidvnc_ng/MainActivity.java index 10563c9..a9b7d50 100644 --- a/app/src/main/java/net/christianbeier/droidvnc_ng/MainActivity.java +++ b/app/src/main/java/net/christianbeier/droidvnc_ng/MainActivity.java @@ -311,7 +311,7 @@ protected void onCreate(Bundle savedInstanceState) { }); - NetworkInterfaceTester nit = new NetworkInterfaceTester(this); + NetworkInterfaceTester nit = NetworkInterfaceTester.getInstance(this); ListenIfAdapter lsif = new ListenIfAdapter(nit, this); final Spinner listenInterfaceSpin = findViewById(R.id.settings_listening_interface); listenInterfaceSpin.setAdapter(lsif); diff --git a/app/src/main/java/net/christianbeier/droidvnc_ng/MainService.java b/app/src/main/java/net/christianbeier/droidvnc_ng/MainService.java index a7fde30..57dc6b8 100644 --- a/app/src/main/java/net/christianbeier/droidvnc_ng/MainService.java +++ b/app/src/main/java/net/christianbeier/droidvnc_ng/MainService.java @@ -63,7 +63,7 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; -public class MainService extends Service { +public class MainService extends Service implements NetworkInterfaceTester.OnNetworkStateChangedListener { private static final String TAG = "MainService"; static final int NOTIFICATION_ID = 11; @@ -225,6 +225,7 @@ public void onCreate() { Log.d(TAG, "onCreate"); instance = this; + NetworkInterfaceTester.getInstance(this).addOnNetworkStateChangedListener(this); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { /* @@ -930,4 +931,15 @@ static Notification getCurrentNotification() { return null; } } + + + + + public void onNetworkStateChanged(NetworkInterfaceTester nit) { + if (isServerActive()) { + if (!nit.isIfEnabled(getListenInterface())) { + this.stopSelf(); + } + } + } } diff --git a/app/src/main/java/net/christianbeier/droidvnc_ng/NetworkInterfaceTester.java b/app/src/main/java/net/christianbeier/droidvnc_ng/NetworkInterfaceTester.java index 4fe2790..27bbc7c 100644 --- a/app/src/main/java/net/christianbeier/droidvnc_ng/NetworkInterfaceTester.java +++ b/app/src/main/java/net/christianbeier/droidvnc_ng/NetworkInterfaceTester.java @@ -41,8 +41,21 @@ public class NetworkInterfaceTester extends ConnectivityManager.NetworkCallback { public static final String TAG = "NetworkInterfaceTester"; + private static NetworkInterfaceTester INSTANCE; + + + + public synchronized static NetworkInterfaceTester getInstance(Context context) { + if (NetworkInterfaceTester.INSTANCE == null) { + NetworkInterfaceTester.INSTANCE = new NetworkInterfaceTester(context); + } + return NetworkInterfaceTester.INSTANCE; + } + + + public static interface OnNetworkStateChangedListener { - public void onNetworkStateChanged(NetworkInterfaceTester nit, NetworkInterface iface, boolean enabled); + public void onNetworkStateChanged(NetworkInterfaceTester nit); } @@ -52,16 +65,16 @@ public static class NetIfData { private String displayName; private String friendlyName; - private NetIfData(Context context) { - this(null, context); + private NetIfData() { + this(null); } - private NetIfData(NetworkInterface nic, Context context) { + private NetIfData(NetworkInterface nic) { this.nic = nic; if (nic == null) { this.name = "0.0.0.0"; - this.displayName = context.getResources().getString(R.string.main_activity_settings_listenif_spin_any); + this.displayName = "Any"; } else { this.name = this.nic.getName(); @@ -70,15 +83,15 @@ private NetIfData(NetworkInterface nic, Context context) { } } - public static NetIfData getAnyOption(Context context) { + public static NetIfData getAnyOption() { if (NetworkInterfaceTester.IF_ANY == null) { - NetworkInterfaceTester.IF_ANY = new NetIfData(context); + NetworkInterfaceTester.IF_ANY = new NetIfData(); } return NetworkInterfaceTester.IF_ANY; } - public static NetIfData getOptionForNic(NetworkInterface nic, Context context) { - return new NetIfData(nic, context); + public static NetIfData getOptionForNic(NetworkInterface nic) { + return new NetIfData(nic); } @@ -113,10 +126,9 @@ public String toString() { - public NetworkInterfaceTester(Context context) { - this.context = context; - + private NetworkInterfaceTester(Context context) { this.netIf = Utils.getAvailableNICs(); + this.netIfSize = this.netIf.size(); this.netIfEnabled = new ArrayList<>(); @@ -146,14 +158,13 @@ public NetworkInterfaceTester(Context context) { - public ArrayList getAvailableInterfaces() { ArrayList ls = new ArrayList<>(); - ls.add(NetIfData.getAnyOption(this.context)); + ls.add(NetIfData.getAnyOption()); for (int i = 0; i < this.netIfSize; i++) { if (this.netIfEnabled.get(i)) { - NetIfData nid = NetIfData.getOptionForNic(this.netIf.get(i), this.context); + NetIfData nid = NetIfData.getOptionForNic(this.netIf.get(i)); ls.add(nid); } } @@ -163,58 +174,99 @@ public ArrayList getAvailableInterfaces() { + private int searchForNICByName(String ifname) { + int i = 0; + boolean found = false; + for (i = 0; !found && i < this.netIfSize; i++) { + if (ifname.equals(this.netIf.get(i).getName())) { + i--; + found = true; + } + } + + return found ? i : -1; + } + + + @Override public void onAvailable(Network network) { super.onAvailable(network); - int i = this.storeNetworkHashCode(network); - this.setEnabled(i, true); - this.updateListener(i, true); + int i = this.storeNetwork(network); + if (i != -1) { + this.setEnabled(i, true); + this.updateListener(i, true); + } } @Override public void onLost(Network network) { super.onLost(network); - int i = this.getFromNetworkHashCode(network); - this.setEnabled(i, false); - this.updateListener(i, false); + int i = this.getFromNetwork(network); + if (i != -1) { + this.setEnabled(i, false); + this.updateListener(i, false); + } + } + + + @Override + public void onLosing(Network network, int maxMsToLive) { + Log.d(TAG, "onLosing " + network); } - private int storeNetworkHashCode(Network network) { + @Override + public void onBlockedStatusChanged(Network network, boolean blocked) { + Log.d(TAG, "onBlockedStatusChagned " + network); + } + + + @Override + public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { + Log.d(TAG, "onLinkPropertiesChanged " + network); + } + + + /* + * The idea is simple: during onLost, LinkProperties attributes are lost + * inside Network, so, memorize a reference to the Network instance, this way, + * we'll be able to understand what exact interface was lost. + */ + private int storeNetwork(Network network) { LinkProperties prop = this.manager.getLinkProperties(network); NetworkInterface iface = null; try { iface = NetworkInterface.getByName(prop.getInterfaceName()); + } catch (SocketException ex) { - // unused - } + Log.d(TAG, "Socket exception has occurred, return -1"); + return -1; - int i = 0; - boolean found = false; - for (i = 0; !found && i < this.netIfSize; i++) { - if (iface.getName().equals(this.netIf.get(i).getName())) { - this.networks.set(i, network); - i--; - found = true; - } } - if (found) { - Log.d(TAG, "Added network " + iface.getName()); + // No socket exception occurred, let's see if the interface is available + int i = this.searchForNICByName(iface.getName()); + if (i == -1) { // if not found, it means that it has been created recently, add to list + this.netIf.add(iface); + this.networks.add(network); + i = this.netIfSize++; } else { - Log.d(TAG, "No network found"); + // Otherwise, the interface exists, replace network + this.networks.set(i, network); } - return found ? i : -1; - } + Log.d(TAG, "Added network " + iface.getName()); + return i; + } - private int getFromNetworkHashCode(Network network) { + private int getFromNetwork(Network network) { int i = this.networks.indexOf(network); if (i != -1) { @@ -233,6 +285,17 @@ private void setEnabled(int i, boolean enabled) { } + public boolean isEnabled(int i) { + return this.netIfEnabled.get(i); + } + + + public boolean isIfEnabled(String ifname) { + int pos = this.searchForNICByName(ifname); + return pos == -1 ? false : this.netIfEnabled.get(pos); + } + + public void addOnNetworkStateChangedListener(OnNetworkStateChangedListener onscl) { @@ -244,7 +307,7 @@ public void addOnNetworkStateChangedListener(OnNetworkStateChangedListener onscl private void updateListener(int i, boolean enabled) { for (OnNetworkStateChangedListener onscl : this.listeners) { - onscl.onNetworkStateChanged(this, this.netIf.get(i), enabled); + onscl.onNetworkStateChanged(this); } } } \ No newline at end of file diff --git a/app/src/main/java/net/christianbeier/droidvnc_ng/Utils.kt b/app/src/main/java/net/christianbeier/droidvnc_ng/Utils.kt index 9ae3d7d..28e032c 100644 --- a/app/src/main/java/net/christianbeier/droidvnc_ng/Utils.kt +++ b/app/src/main/java/net/christianbeier/droidvnc_ng/Utils.kt @@ -133,7 +133,7 @@ object Utils { @JvmStatic fun getAvailableNICs(): ArrayList { - return getAvailableNICs(true, true); + return getAvailableNICs(false, true); }