diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml
index 861112c1c..bd55734cb 100644
--- a/F-Droid/res/values/strings.xml
+++ b/F-Droid/res/values/strings.xml
@@ -322,6 +322,7 @@
Not visible via Bluetooth
Visible via Wi-Fi
Setting up Wi-Fi…
+ Stopping Wi-Fi…
Not visible via Wi-Fi
Device Name
Can\'t find who you\'re looking for?
diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java b/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java
index 0dc8380c3..7203aefa5 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java
@@ -441,14 +441,18 @@ public class SwapService extends Service {
/**
* Handles checking if the {@link SwapService} is running, and only restarts it if it was running.
*/
- public void restartWifiIfEnabled() {
+ public void stopWifiIfEnabled(final boolean restartAfterStopping) {
if (wifiSwap.isConnected()) {
new AsyncTask() {
@Override
protected Void doInBackground(Void... params) {
- Utils.debugLog(TAG, "Restarting WiFi swap service");
+ Utils.debugLog(TAG, "Stopping the currently running WiFi swap service (on background thread)");
wifiSwap.stop();
- wifiSwap.start();
+
+ if (restartAfterStopping) {
+ Utils.debugLog(TAG, "Restarting WiFi swap service after stopping (still on background thread)");
+ wifiSwap.start();
+ }
return null;
}
}.execute();
@@ -483,6 +487,7 @@ public class SwapService extends Service {
public static final String WIFI_STATE_CHANGE = "org.fdroid.fdroid.WIFI_STATE_CHANGE";
public static final String EXTRA_STARTING = "STARTING";
public static final String EXTRA_STARTED = "STARTED";
+ public static final String EXTRA_STOPPING = "STOPPING";
public static final String EXTRA_STOPPED = "STOPPED";
private static final int NOTIFICATION = 1;
@@ -632,7 +637,7 @@ public class SwapService extends Service {
@Override
public void onPreferenceChange() {
Log.i(TAG, "Swap over HTTPS preference changed.");
- restartWifiIfEnabled();
+ stopWifiIfEnabled(true);
}
};
@@ -640,7 +645,8 @@ public class SwapService extends Service {
private final BroadcastReceiver onWifiChange = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent i) {
- restartWifiIfEnabled();
+ boolean hasIp = FDroidApp.ipAddressString != null;
+ stopWifiIfEnabled(hasIp);
}
};
diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/type/BonjourBroadcast.java b/F-Droid/src/org/fdroid/fdroid/localrepo/type/BonjourBroadcast.java
index a5ab3a6b9..11711ee49 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/type/BonjourBroadcast.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/type/BonjourBroadcast.java
@@ -1,6 +1,7 @@
package org.fdroid.fdroid.localrepo.type;
import android.content.Context;
+import android.support.annotation.Nullable;
import android.util.Log;
import org.fdroid.fdroid.FDroidApp;
@@ -10,6 +11,7 @@ import org.fdroid.fdroid.localrepo.SwapService;
import java.io.IOException;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.HashMap;
import javax.jmdns.JmDNS;
@@ -31,10 +33,15 @@ public class BonjourBroadcast extends SwapType {
@Override
public void start() {
-
Utils.debugLog(TAG, "Preparing to start Bonjour service.");
sendBroadcast(SwapService.EXTRA_STARTING);
+ InetAddress address = getDeviceAddress();
+ if (address == null) {
+ Log.e(TAG, "Starting Bonjour service, but couldn't ascertain IP address. Seems we are not connected to a network.");
+ return;
+ }
+
/*
* a ServiceInfo can only be registered with a single instance
* of JmDNS, and there is only ever a single LocalHTTPD port to
@@ -43,6 +50,7 @@ public class BonjourBroadcast extends SwapType {
if (pairService != null || jmdns != null) {
clearCurrentMDNSService();
}
+
String repoName = Preferences.get().getLocalRepoName();
HashMap values = new HashMap<>();
values.put("path", "/fdroid/repo");
@@ -59,7 +67,7 @@ public class BonjourBroadcast extends SwapType {
try {
Utils.debugLog(TAG, "Starting bonjour service...");
pairService = ServiceInfo.create(type, repoName, FDroidApp.port, 0, 0, values);
- jmdns = JmDNS.create(InetAddress.getByName(FDroidApp.ipAddressString));
+ jmdns = JmDNS.create(address);
jmdns.registerService(pairService);
setConnected(true);
Utils.debugLog(TAG, "... Bounjour service started.");
@@ -78,12 +86,9 @@ public class BonjourBroadcast extends SwapType {
private void clearCurrentMDNSService() {
if (jmdns != null) {
- if (pairService != null) {
- jmdns.unregisterService(pairService);
- pairService = null;
- }
jmdns.unregisterAllServices();
Utils.closeQuietly(jmdns);
+ pairService = null;
jmdns = null;
}
}
@@ -93,4 +98,15 @@ public class BonjourBroadcast extends SwapType {
return SwapService.BONJOUR_STATE_CHANGE;
}
+ @Nullable
+ private InetAddress getDeviceAddress() {
+ if (FDroidApp.ipAddressString != null) {
+ try {
+ return InetAddress.getByName(FDroidApp.ipAddressString);
+ } catch (UnknownHostException e) { }
+ }
+
+ return null;
+ }
+
}
diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/type/SwapType.java b/F-Droid/src/org/fdroid/fdroid/localrepo/type/SwapType.java
index 304be4cbb..1a2ee1a78 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/type/SwapType.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/type/SwapType.java
@@ -99,7 +99,7 @@ public abstract class SwapType {
public void run() {
SwapType.this.stop();
}
- }.run();
+ }.start();
}
}
diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/type/WifiSwap.java b/F-Droid/src/org/fdroid/fdroid/localrepo/type/WifiSwap.java
index d66e5ed33..8229f692f 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/type/WifiSwap.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/type/WifiSwap.java
@@ -19,6 +19,13 @@ import java.io.IOException;
import java.net.BindException;
import java.util.Random;
+import rx.Single;
+import rx.SingleSubscriber;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.functions.Action1;
+import rx.functions.Func2;
+import rx.schedulers.Schedulers;
+
public class WifiSwap extends SwapType {
private static final String TAG = "WifiSwap";
@@ -42,55 +49,115 @@ public class WifiSwap extends SwapType {
@Override
public void start() {
-
Utils.debugLog(TAG, "Preparing swap webserver.");
sendBroadcast(SwapService.EXTRA_STARTING);
- Runnable webServer = new Runnable() {
- // Tell Eclipse this is not a leak because of Looper use.
- @SuppressLint("HandlerLeak")
- @Override
- public void run() {
- localHttpd = new LocalHTTPD(
- context,
- FDroidApp.ipAddressString,
- FDroidApp.port,
- context.getFilesDir(),
- Preferences.get().isLocalRepoHttpsEnabled());
+ if (FDroidApp.ipAddressString == null) {
+ Log.e(TAG, "Not starting swap webserver, because we don't seem to be connected to a network.");
+ setConnected(false);
+ }
- Looper.prepare(); // must be run before creating a Handler
- webServerThreadHandler = new Handler() {
+ Single.zip(
+ Single.create(getWebServerTask()),
+ Single.create(getBonjourTask()),
+ new Func2() {
@Override
- public void handleMessage(Message msg) {
- Log.i(TAG, "we've been asked to stop the webserver: " + msg.obj);
- setConnected(false);
- localHttpd.stop();
+ public Boolean call(Boolean webServerTask, Boolean bonjourServiceTask) {
+ return bonjourServiceTask && webServerTask;
}
- };
- try {
- Utils.debugLog(TAG, "Starting swap webserver...");
- localHttpd.start();
- setConnected(true);
- Utils.debugLog(TAG, "Swap webserver started.");
- } catch (BindException e) {
- int prev = FDroidApp.port;
- FDroidApp.port = FDroidApp.port + new Random().nextInt(1111);
- setConnected(false);
- Log.w(TAG, "port " + prev + " occupied, trying on " + FDroidApp.port + "!");
- context.startService(new Intent(context, WifiStateChangeService.class));
- } catch (IOException e) {
- setConnected(false);
- Log.e(TAG, "Could not start local repo HTTP server", e);
- }
- Looper.loop(); // start the message receiving loop
+ })
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribeOn(Schedulers.newThread())
+ .subscribe(new Action1() {
+ @Override
+ public void call(Boolean success) {
+ setConnected(success);
+ }
+ },
+ new Action1() {
+ @Override
+ public void call(Throwable throwable) {
+ setConnected(false);
+ }
+ });
+ }
+
+ /**
+ * A task which starts the {@link WifiSwap#bonjourBroadcast} and then emits a `true` value at
+ * the end.
+ */
+ private Single.OnSubscribe getBonjourTask() {
+ return new Single.OnSubscribe() {
+ @Override
+ public void call(SingleSubscriber super Boolean> singleSubscriber) {
+ bonjourBroadcast.start();
+
+ // TODO: Be more intelligent about failures here so that we can invoke
+ // singleSubscriber.onError() in the appropriate circumstances.
+ singleSubscriber.onSuccess(true);
+ }
+ };
+ }
+
+ /**
+ * Constructs a new {@link Thread} for the webserver to run on. If successful, it will also
+ * populate the webServerThreadHandler property and bind it to that particular thread. This
+ * allows messages to be sent to the webserver thread by posting messages to that handler.
+ */
+ private Single.OnSubscribe getWebServerTask() {
+ return new Single.OnSubscribe() {
+ @Override
+ public void call(final SingleSubscriber super Boolean> singleSubscriber) {
+ new Thread(new Runnable() {
+ // Tell Eclipse this is not a leak because of Looper use.
+ @SuppressLint("HandlerLeak")
+ @Override
+ public void run() {
+ localHttpd = new LocalHTTPD(
+ context,
+ FDroidApp.ipAddressString,
+ FDroidApp.port,
+ context.getFilesDir(),
+ Preferences.get().isLocalRepoHttpsEnabled());
+
+ Looper.prepare(); // must be run before creating a Handler
+ webServerThreadHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ Log.i(TAG, "we've been asked to stop the webserver: " + msg.obj);
+ localHttpd.stop();
+ Looper looper = Looper.myLooper();
+ if (looper == null) {
+ Log.e(TAG, "Looper.myLooper() was null for sum reason while shutting down the swap webserver.");
+ } else {
+ looper.quit();
+ }
+ }
+ };
+ try {
+ Utils.debugLog(TAG, "Starting swap webserver...");
+ localHttpd.start();
+ Utils.debugLog(TAG, "Swap webserver started.");
+ singleSubscriber.onSuccess(true);
+ } catch (BindException e) {
+ int prev = FDroidApp.port;
+ FDroidApp.port = FDroidApp.port + new Random().nextInt(1111);
+ context.startService(new Intent(context, WifiStateChangeService.class));
+ singleSubscriber.onError(new Exception("port " + prev + " occupied, trying on " + FDroidApp.port + "!"));
+ } catch (IOException e) {
+ Log.e(TAG, "Could not start local repo HTTP server", e);
+ singleSubscriber.onError(e);
+ }
+ Looper.loop(); // start the message receiving loop
+ }
+ }).start();
}
};
- new Thread(webServer).start();
- bonjourBroadcast.start();
}
@Override
public void stop() {
+ sendBroadcast(SwapService.EXTRA_STOPPING);
if (webServerThreadHandler == null) {
Log.i(TAG, "null handler in stopWebServer");
} else {
@@ -99,7 +166,13 @@ public class WifiSwap extends SwapType {
msg.obj = webServerThreadHandler.getLooper().getThread().getName() + " says stop";
webServerThreadHandler.sendMessage(msg);
}
+
+ // Stop the Bonjour stuff after asking the webserver to stop. This is not required in this
+ // order, but it helps. In practice, the Bonjour stuff takes a second or two to stop. This
+ // should give enough time for the message we posted above to reach the web server thread
+ // and for the webserver to thus be stopped.
bonjourBroadcast.stop();
+ setConnected(false);
}
}
diff --git a/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java b/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java
index 0e18b6af7..ccf162460 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java
@@ -181,7 +181,7 @@ public class WifiStateChangeService extends Service {
getApplicationContext().bindService(swapService, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- ((SwapService.Binder) service).getService().restartWifiIfEnabled();
+ ((SwapService.Binder) service).getService().stopWifiIfEnabled(true);
getApplicationContext().unbindService(this);
}
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/StartSwapView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/StartSwapView.java
index bbc17b184..2923865cf 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/StartSwapView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/StartSwapView.java
@@ -302,9 +302,8 @@ public class StartSwapView extends ScrollView implements SwapWorkflowActivity.In
Utils.debugLog(TAG, "Received onCheckChanged(true) for WiFi swap, asking in background thread to ensure WiFi swap is running.");
getManager().getWifiSwap().ensureRunningInBackground();
} else {
- Utils.debugLog(TAG, "Received onCheckChanged(false) for WiFi swap, disabling WiFi swap.");
- getManager().getWifiSwap().stop();
- Utils.debugLog(TAG, "Received onCheckChanged(false) for WiFi swap, WiFi swap disabled successfully.");
+ Utils.debugLog(TAG, "Received onCheckChanged(false) for WiFi swap, disabling WiFi swap in background thread.");
+ getManager().getWifiSwap().stopInBackground();
}
uiUpdateWifiNetwork();
}
@@ -323,10 +322,15 @@ public class StartSwapView extends ScrollView implements SwapWorkflowActivity.In
@Override
public void onReceive(Context context, Intent intent) {
if (intent.hasExtra(SwapService.EXTRA_STARTING)) {
- Utils.debugLog(TAG, "WiFi service is starting (setting toggle to visible, but disabled).");
+ Utils.debugLog(TAG, "WiFi service is starting (setting toggle to checked, but disabled).");
textWifiVisible.setText(R.string.swap_setting_up_wifi);
wifiSwitch.setEnabled(false);
wifiSwitch.setChecked(true);
+ } else if (intent.hasExtra(SwapService.EXTRA_STOPPING)) {
+ Utils.debugLog(TAG, "WiFi service is stopping (setting toggle to unchecked and disabled).");
+ textWifiVisible.setText(R.string.swap_stopping_wifi);
+ wifiSwitch.setEnabled(false);
+ wifiSwitch.setChecked(false);
} else {
wifiSwitch.setEnabled(true);
if (intent.hasExtra(SwapService.EXTRA_STARTED)) {