create BonjourManager to manage jmdns in HandlerThread, with tests
This commit is contained in:
parent
79e7e78e7f
commit
46472ba7a4
@ -0,0 +1,125 @@
|
|||||||
|
package org.fdroid.fdroid.localrepo;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import javax.jmdns.ServiceEvent;
|
||||||
|
import javax.jmdns.ServiceListener;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class BonjourManagerTest {
|
||||||
|
|
||||||
|
private static final String NAME = "Robolectric-test";
|
||||||
|
private static final String LOCALHOST = "localhost";
|
||||||
|
private static final int PORT = 8888;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartStop() throws InterruptedException {
|
||||||
|
Context context = InstrumentationRegistry.getTargetContext();
|
||||||
|
|
||||||
|
FDroidApp.ipAddressString = LOCALHOST;
|
||||||
|
FDroidApp.port = PORT;
|
||||||
|
|
||||||
|
final CountDownLatch addedLatch = new CountDownLatch(1);
|
||||||
|
final CountDownLatch resolvedLatch = new CountDownLatch(1);
|
||||||
|
final CountDownLatch removedLatch = new CountDownLatch(1);
|
||||||
|
BonjourManager.start(context, NAME, false,
|
||||||
|
new ServiceListener() {
|
||||||
|
@Override
|
||||||
|
public void serviceAdded(ServiceEvent serviceEvent) {
|
||||||
|
System.out.println("Service added: " + serviceEvent.getInfo());
|
||||||
|
if (NAME.equals(serviceEvent.getName())) {
|
||||||
|
addedLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serviceRemoved(ServiceEvent serviceEvent) {
|
||||||
|
System.out.println("Service removed: " + serviceEvent.getInfo());
|
||||||
|
removedLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serviceResolved(ServiceEvent serviceEvent) {
|
||||||
|
System.out.println("Service resolved: " + serviceEvent.getInfo());
|
||||||
|
if (NAME.equals(serviceEvent.getName())) {
|
||||||
|
resolvedLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, getBlankServiceListener());
|
||||||
|
BonjourManager.setVisible(context, true);
|
||||||
|
assertTrue(addedLatch.await(30, TimeUnit.SECONDS));
|
||||||
|
assertTrue(resolvedLatch.await(30, TimeUnit.SECONDS));
|
||||||
|
BonjourManager.setVisible(context, false);
|
||||||
|
assertTrue(removedLatch.await(30, TimeUnit.SECONDS));
|
||||||
|
BonjourManager.stop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestart() throws InterruptedException {
|
||||||
|
Context context = InstrumentationRegistry.getTargetContext();
|
||||||
|
|
||||||
|
FDroidApp.ipAddressString = LOCALHOST;
|
||||||
|
FDroidApp.port = PORT;
|
||||||
|
|
||||||
|
BonjourManager.start(context, NAME, false, getBlankServiceListener(), getBlankServiceListener());
|
||||||
|
|
||||||
|
final CountDownLatch addedLatch = new CountDownLatch(1);
|
||||||
|
final CountDownLatch resolvedLatch = new CountDownLatch(1);
|
||||||
|
final CountDownLatch removedLatch = new CountDownLatch(1);
|
||||||
|
BonjourManager.restart(context, NAME, false,
|
||||||
|
new ServiceListener() {
|
||||||
|
@Override
|
||||||
|
public void serviceAdded(ServiceEvent serviceEvent) {
|
||||||
|
System.out.println("Service added: " + serviceEvent.getInfo());
|
||||||
|
if (NAME.equals(serviceEvent.getName())) {
|
||||||
|
addedLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serviceRemoved(ServiceEvent serviceEvent) {
|
||||||
|
System.out.println("Service removed: " + serviceEvent.getInfo());
|
||||||
|
removedLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serviceResolved(ServiceEvent serviceEvent) {
|
||||||
|
System.out.println("Service resolved: " + serviceEvent.getInfo());
|
||||||
|
if (NAME.equals(serviceEvent.getName())) {
|
||||||
|
resolvedLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, getBlankServiceListener());
|
||||||
|
BonjourManager.setVisible(context, true);
|
||||||
|
assertTrue(addedLatch.await(30, TimeUnit.SECONDS));
|
||||||
|
assertTrue(resolvedLatch.await(30, TimeUnit.SECONDS));
|
||||||
|
BonjourManager.setVisible(context, false);
|
||||||
|
assertTrue(removedLatch.await(30, TimeUnit.SECONDS));
|
||||||
|
BonjourManager.stop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceListener getBlankServiceListener() {
|
||||||
|
return new ServiceListener() {
|
||||||
|
@Override
|
||||||
|
public void serviceAdded(ServiceEvent serviceEvent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serviceRemoved(ServiceEvent serviceEvent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serviceResolved(ServiceEvent serviceEvent) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,269 @@
|
|||||||
|
package org.fdroid.fdroid.localrepo;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerThread;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
|
import org.fdroid.fdroid.Preferences;
|
||||||
|
import org.fdroid.fdroid.Utils;
|
||||||
|
|
||||||
|
import javax.jmdns.JmDNS;
|
||||||
|
import javax.jmdns.ServiceEvent;
|
||||||
|
import javax.jmdns.ServiceInfo;
|
||||||
|
import javax.jmdns.ServiceListener;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage {@link JmDNS} in a {@link HandlerThread}. The start process is in
|
||||||
|
* {@link HandlerThread#onLooperPrepared()} so that it is always started before
|
||||||
|
* any messages get delivered from the queue.
|
||||||
|
*/
|
||||||
|
public class BonjourManager {
|
||||||
|
private static final String TAG = "BonjourManager";
|
||||||
|
|
||||||
|
public static final String ACTION_ADDED = "BonjourAdded";
|
||||||
|
public static final String ACTION_RESOLVED = "BonjourResolved";
|
||||||
|
public static final String ACTION_REMOVED = "BonjourRemoved";
|
||||||
|
|
||||||
|
public static final String ACTION_STATUS = "BonjourStatus";
|
||||||
|
public static final String EXTRA_STATUS = "BonjourStatusExtra";
|
||||||
|
public static final int STATUS_STARTING = 0;
|
||||||
|
public static final int STATUS_STARTED = 1;
|
||||||
|
public static final int STATUS_STOPPING = 2;
|
||||||
|
public static final int STATUS_STOPPED = 3;
|
||||||
|
public static final int STATUS_VISIBLE = 4;
|
||||||
|
public static final int STATUS_NOT_VISIBLE = 5;
|
||||||
|
public static final int STATUS_ERROR = 0xffff;
|
||||||
|
|
||||||
|
public static final String HTTP_SERVICE_TYPE = "_http._tcp.local.";
|
||||||
|
public static final String HTTPS_SERVICE_TYPE = "_https._tcp.local.";
|
||||||
|
|
||||||
|
private static final int STOP = 5709;
|
||||||
|
private static final int VISIBLE = 4151873;
|
||||||
|
private static final int NOT_VISIBLE = 144151873;
|
||||||
|
|
||||||
|
private static WeakReference<Context> context;
|
||||||
|
private static Handler handler;
|
||||||
|
private static volatile HandlerThread handlerThread;
|
||||||
|
private static ServiceInfo pairService;
|
||||||
|
private static JmDNS jmdns;
|
||||||
|
|
||||||
|
public static boolean isAlive() {
|
||||||
|
return handlerThread != null && handlerThread.isAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the Bonjour/mDNS, triggering a status broadcast via {@link #ACTION_STATUS}.
|
||||||
|
* {@link #STATUS_STOPPED} can be broadcast multiple times for the same session,
|
||||||
|
* so make sure {@link android.content.BroadcastReceiver}s handle duplicates.
|
||||||
|
*/
|
||||||
|
public static void stop(Context context) {
|
||||||
|
BonjourManager.context = new WeakReference<>(context);
|
||||||
|
if (handler == null || handlerThread == null || !handlerThread.isAlive()) {
|
||||||
|
sendBroadcast(STATUS_STOPPED, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendBroadcast(STATUS_STOPPING, null);
|
||||||
|
handler.sendEmptyMessage(STOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setVisible(Context context, boolean visible) {
|
||||||
|
BonjourManager.context = new WeakReference<>(context);
|
||||||
|
if (handler == null || handlerThread == null || !handlerThread.isAlive()) {
|
||||||
|
Log.e(TAG, "handlerThread is stopped, not changing visibility!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (visible) {
|
||||||
|
handler.sendEmptyMessage(VISIBLE);
|
||||||
|
} else {
|
||||||
|
handler.sendEmptyMessage(NOT_VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the service, triggering a status broadcast via {@link #ACTION_STATUS}.
|
||||||
|
* {@link #STATUS_STARTED} can be broadcast multiple times for the same session,
|
||||||
|
* so make sure {@link android.content.BroadcastReceiver}s handle duplicates.
|
||||||
|
*/
|
||||||
|
public static void start(Context context) {
|
||||||
|
start(context,
|
||||||
|
Preferences.get().getLocalRepoName(),
|
||||||
|
Preferences.get().isLocalRepoHttpsEnabled(),
|
||||||
|
httpServiceListener, httpsServiceListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testable version, not for regular use.
|
||||||
|
*
|
||||||
|
* @see #start(Context)
|
||||||
|
*/
|
||||||
|
static void start(final Context context,
|
||||||
|
final String localRepoName, final boolean useHttps,
|
||||||
|
final ServiceListener httpServiceListener, final ServiceListener httpsServiceListener) {
|
||||||
|
BonjourManager.context = new WeakReference<>(context);
|
||||||
|
if (handlerThread != null && handlerThread.isAlive()) {
|
||||||
|
sendBroadcast(STATUS_STARTED, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendBroadcast(STATUS_STARTING, null);
|
||||||
|
|
||||||
|
handlerThread = new HandlerThread("BonjourManager", Process.THREAD_PRIORITY_LESS_FAVORABLE) {
|
||||||
|
@Override
|
||||||
|
protected void onLooperPrepared() {
|
||||||
|
try {
|
||||||
|
InetAddress address = InetAddress.getByName(FDroidApp.ipAddressString);
|
||||||
|
jmdns = JmDNS.create(address);
|
||||||
|
jmdns.addServiceListener(HTTP_SERVICE_TYPE, httpServiceListener);
|
||||||
|
jmdns.addServiceListener(HTTPS_SERVICE_TYPE, httpsServiceListener);
|
||||||
|
sendBroadcast(STATUS_STARTED, null);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (handler != null) {
|
||||||
|
handler.removeMessages(VISIBLE);
|
||||||
|
handler.sendMessageAtFrontOfQueue(handler.obtainMessage(STOP));
|
||||||
|
}
|
||||||
|
Log.e(TAG, "Error while registering jmdns service", e);
|
||||||
|
sendBroadcast(STATUS_ERROR, e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
handlerThread.start();
|
||||||
|
handler = new Handler(handlerThread.getLooper()) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case VISIBLE:
|
||||||
|
handleVisible(localRepoName, useHttps);
|
||||||
|
break;
|
||||||
|
case NOT_VISIBLE:
|
||||||
|
handleNotVisible();
|
||||||
|
break;
|
||||||
|
case STOP:
|
||||||
|
handleStop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleVisible(String localRepoName, boolean useHttps) {
|
||||||
|
HashMap<String, String> values = new HashMap<>();
|
||||||
|
values.put("path", "/fdroid/repo");
|
||||||
|
values.put("name", localRepoName);
|
||||||
|
values.put("fingerprint", FDroidApp.repo.fingerprint);
|
||||||
|
String type;
|
||||||
|
if (useHttps) {
|
||||||
|
values.put("type", "fdroidrepos");
|
||||||
|
type = HTTPS_SERVICE_TYPE;
|
||||||
|
} else {
|
||||||
|
values.put("type", "fdroidrepo");
|
||||||
|
type = HTTP_SERVICE_TYPE;
|
||||||
|
}
|
||||||
|
ServiceInfo newPairService = ServiceInfo.create(type, localRepoName, FDroidApp.port, 0, 0, values);
|
||||||
|
if (!newPairService.equals(pairService)) try {
|
||||||
|
if (pairService != null) {
|
||||||
|
jmdns.unregisterService(pairService);
|
||||||
|
}
|
||||||
|
jmdns.registerService(newPairService);
|
||||||
|
pairService = newPairService;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
sendBroadcast(STATUS_ERROR, e.getLocalizedMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendBroadcast(STATUS_VISIBLE, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleNotVisible() {
|
||||||
|
if (pairService != null) {
|
||||||
|
jmdns.unregisterService(pairService);
|
||||||
|
pairService = null;
|
||||||
|
}
|
||||||
|
sendBroadcast(STATUS_NOT_VISIBLE, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleStop() {
|
||||||
|
if (jmdns != null) {
|
||||||
|
jmdns.unregisterAllServices();
|
||||||
|
Utils.closeQuietly(jmdns);
|
||||||
|
pairService = null;
|
||||||
|
jmdns = null;
|
||||||
|
}
|
||||||
|
handlerThread.quit();
|
||||||
|
handlerThread = null;
|
||||||
|
sendBroadcast(STATUS_STOPPED, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void restart(Context context) {
|
||||||
|
restart(context,
|
||||||
|
Preferences.get().getLocalRepoName(),
|
||||||
|
Preferences.get().isLocalRepoHttpsEnabled(),
|
||||||
|
httpServiceListener, httpsServiceListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testable version, not for regular use.
|
||||||
|
*
|
||||||
|
* @see #restart(Context)
|
||||||
|
*/
|
||||||
|
static void restart(final Context context,
|
||||||
|
final String localRepoName, final boolean useHttps,
|
||||||
|
final ServiceListener httpServiceListener, final ServiceListener httpsServiceListener) {
|
||||||
|
stop(context);
|
||||||
|
try {
|
||||||
|
handlerThread.join(10000);
|
||||||
|
} catch (InterruptedException | NullPointerException e) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
start(context, localRepoName, useHttps, httpServiceListener, httpsServiceListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendBroadcast(String action, String message) {
|
||||||
|
Intent intent = new Intent(action);
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, message);
|
||||||
|
LocalBroadcastManager.getInstance(context.get()).sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendBroadcast(int status, String message) {
|
||||||
|
|
||||||
|
Intent intent = new Intent(ACTION_STATUS);
|
||||||
|
intent.putExtra(EXTRA_STATUS, status);
|
||||||
|
if (!TextUtils.isEmpty(message)) {
|
||||||
|
intent.putExtra(Intent.EXTRA_TEXT, message);
|
||||||
|
}
|
||||||
|
LocalBroadcastManager.getInstance(context.get()).sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ServiceListener httpServiceListener = new SwapServiceListener();
|
||||||
|
private static final ServiceListener httpsServiceListener = new SwapServiceListener();
|
||||||
|
|
||||||
|
private static class SwapServiceListener implements ServiceListener {
|
||||||
|
@Override
|
||||||
|
public void serviceAdded(ServiceEvent serviceEvent) {
|
||||||
|
Utils.debugLog(TAG, "Service added: " + serviceEvent.getInfo());
|
||||||
|
sendBroadcast(ACTION_ADDED, serviceEvent.getInfo().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serviceRemoved(ServiceEvent serviceEvent) {
|
||||||
|
Utils.debugLog(TAG, "Service removed: " + serviceEvent.getInfo());
|
||||||
|
sendBroadcast(ACTION_REMOVED, serviceEvent.getInfo().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serviceResolved(ServiceEvent serviceEvent) {
|
||||||
|
Utils.debugLog(TAG, "Service resolved: " + serviceEvent.getInfo());
|
||||||
|
sendBroadcast(ACTION_RESOLVED, serviceEvent.getInfo().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -369,10 +369,6 @@ public class SwapService extends Service {
|
|||||||
return bluetoothSwap.isDiscoverable();
|
return bluetoothSwap.isDiscoverable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBonjourDiscoverable() {
|
|
||||||
return wifiSwap.isConnected() && wifiSwap.getBonjour().isConnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// Old SwapService stuff being merged into that.
|
// Old SwapService stuff being merged into that.
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
|
@ -16,6 +16,9 @@ import javax.jmdns.ServiceListener;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
import static org.fdroid.fdroid.localrepo.BonjourManager.HTTPS_SERVICE_TYPE;
|
||||||
|
import static org.fdroid.fdroid.localrepo.BonjourManager.HTTP_SERVICE_TYPE;
|
||||||
|
|
||||||
@SuppressWarnings("LineLength")
|
@SuppressWarnings("LineLength")
|
||||||
final class BonjourFinder extends PeerFinder implements ServiceListener {
|
final class BonjourFinder extends PeerFinder implements ServiceListener {
|
||||||
|
|
||||||
@ -39,9 +42,6 @@ final class BonjourFinder extends PeerFinder implements ServiceListener {
|
|||||||
|
|
||||||
private static final String TAG = "BonjourFinder";
|
private static final String TAG = "BonjourFinder";
|
||||||
|
|
||||||
private static final String HTTP_SERVICE_TYPE = "_http._tcp.local.";
|
|
||||||
private static final String HTTPS_SERVICE_TYPE = "_https._tcp.local.";
|
|
||||||
|
|
||||||
private JmDNS jmdns;
|
private JmDNS jmdns;
|
||||||
private WifiManager wifiManager;
|
private WifiManager wifiManager;
|
||||||
private WifiManager.MulticastLock multicastLock;
|
private WifiManager.MulticastLock multicastLock;
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
package org.fdroid.fdroid.localrepo.type;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.util.Log;
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
|
||||||
import org.fdroid.fdroid.Utils;
|
|
||||||
import org.fdroid.fdroid.localrepo.SwapService;
|
|
||||||
|
|
||||||
import javax.jmdns.JmDNS;
|
|
||||||
import javax.jmdns.ServiceInfo;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a {@link SwapService#BONJOUR_STATE_CHANGE} broadcasts when starting, started or stopped.
|
|
||||||
*/
|
|
||||||
public class BonjourBroadcast extends SwapType {
|
|
||||||
|
|
||||||
private static final String TAG = "BonjourBroadcast";
|
|
||||||
|
|
||||||
private JmDNS jmdns;
|
|
||||||
private ServiceInfo pairService;
|
|
||||||
|
|
||||||
public BonjourBroadcast(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
|
||||||
* advertise anyway.
|
|
||||||
*/
|
|
||||||
if (pairService != null || jmdns != null) {
|
|
||||||
clearCurrentMDNSService();
|
|
||||||
}
|
|
||||||
|
|
||||||
String repoName = Preferences.get().getLocalRepoName();
|
|
||||||
HashMap<String, String> values = new HashMap<>();
|
|
||||||
values.put("path", "/fdroid/repo");
|
|
||||||
values.put("name", repoName);
|
|
||||||
values.put("fingerprint", FDroidApp.repo.fingerprint);
|
|
||||||
String type;
|
|
||||||
if (Preferences.get().isLocalRepoHttpsEnabled()) {
|
|
||||||
values.put("type", "fdroidrepos");
|
|
||||||
type = "_https._tcp.local.";
|
|
||||||
} else {
|
|
||||||
values.put("type", "fdroidrepo");
|
|
||||||
type = "_http._tcp.local.";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Utils.debugLog(TAG, "Starting bonjour service...");
|
|
||||||
pairService = ServiceInfo.create(type, repoName, FDroidApp.port, 0, 0, values);
|
|
||||||
jmdns = JmDNS.create(address);
|
|
||||||
jmdns.registerService(pairService);
|
|
||||||
setConnected(true);
|
|
||||||
Utils.debugLog(TAG, "... Bounjour service started.");
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "Error while registering jmdns service", e);
|
|
||||||
setConnected(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() {
|
|
||||||
Utils.debugLog(TAG, "Unregistering MDNS service...");
|
|
||||||
clearCurrentMDNSService();
|
|
||||||
setConnected(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearCurrentMDNSService() {
|
|
||||||
if (jmdns != null) {
|
|
||||||
jmdns.unregisterAllServices();
|
|
||||||
Utils.closeQuietly(jmdns);
|
|
||||||
pairService = null;
|
|
||||||
jmdns = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getBroadcastAction() {
|
|
||||||
return SwapService.BONJOUR_STATE_CHANGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private InetAddress getDeviceAddress() {
|
|
||||||
if (FDroidApp.ipAddressString != null) {
|
|
||||||
try {
|
|
||||||
return InetAddress.getByName(FDroidApp.ipAddressString);
|
|
||||||
} catch (UnknownHostException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -2,29 +2,20 @@ package org.fdroid.fdroid.localrepo.type;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.util.Log;
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.localrepo.BonjourManager;
|
||||||
import org.fdroid.fdroid.localrepo.LocalHTTPDManager;
|
import org.fdroid.fdroid.localrepo.LocalHTTPDManager;
|
||||||
import org.fdroid.fdroid.localrepo.SwapService;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
import rx.Single;
|
|
||||||
import rx.SingleSubscriber;
|
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
|
||||||
import rx.functions.Action1;
|
|
||||||
import rx.functions.Func2;
|
|
||||||
import rx.schedulers.Schedulers;
|
|
||||||
|
|
||||||
@SuppressWarnings("LineLength")
|
@SuppressWarnings("LineLength")
|
||||||
public class WifiSwap extends SwapType {
|
public class WifiSwap extends SwapType {
|
||||||
|
|
||||||
private static final String TAG = "WifiSwap";
|
private static final String TAG = "WifiSwap";
|
||||||
|
|
||||||
private final BonjourBroadcast bonjourBroadcast;
|
|
||||||
private final WifiManager wifiManager;
|
private final WifiManager wifiManager;
|
||||||
|
|
||||||
public WifiSwap(Context context, WifiManager wifiManager) {
|
public WifiSwap(Context context, WifiManager wifiManager) {
|
||||||
super(context);
|
super(context);
|
||||||
bonjourBroadcast = new BonjourBroadcast(context);
|
|
||||||
this.wifiManager = wifiManager;
|
this.wifiManager = wifiManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,89 +23,26 @@ public class WifiSwap extends SwapType {
|
|||||||
return SwapService.WIFI_STATE_CHANGE;
|
return SwapService.WIFI_STATE_CHANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BonjourBroadcast getBonjour() {
|
|
||||||
return bonjourBroadcast;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
sendBroadcast(SwapService.EXTRA_STARTING);
|
sendBroadcast(SwapService.EXTRA_STARTING);
|
||||||
wifiManager.setWifiEnabled(true);
|
wifiManager.setWifiEnabled(true);
|
||||||
|
|
||||||
LocalHTTPDManager.start(context);
|
LocalHTTPDManager.start(context);
|
||||||
|
BonjourManager.start(context);
|
||||||
|
BonjourManager.setVisible(context, SwapService.getWifiVisibleUserPreference());
|
||||||
|
|
||||||
if (FDroidApp.ipAddressString == null) {
|
if (FDroidApp.ipAddressString == null) {
|
||||||
Log.e(TAG, "Not starting swap webserver, because we don't seem to be connected to a network.");
|
|
||||||
setConnected(false);
|
setConnected(false);
|
||||||
|
} else {
|
||||||
|
setConnected(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Single.zip(
|
|
||||||
Single.create(getWebServerTask()),
|
|
||||||
Single.create(getBonjourTask()),
|
|
||||||
new Func2<Boolean, Boolean, Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean call(Boolean webServerTask, Boolean bonjourServiceTask) {
|
|
||||||
return bonjourServiceTask && webServerTask;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribeOn(Schedulers.newThread())
|
|
||||||
.subscribe(new Action1<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void call(Boolean success) {
|
|
||||||
setConnected(success);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Action1<Throwable>() {
|
|
||||||
@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<Boolean> getBonjourTask() {
|
|
||||||
return new Single.OnSubscribe<Boolean>() {
|
|
||||||
@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<Boolean> getWebServerTask() {
|
|
||||||
return new Single.OnSubscribe<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void call(SingleSubscriber<? super Boolean> singleSubscriber) {
|
|
||||||
singleSubscriber.onSuccess(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
sendBroadcast(SwapService.EXTRA_STOPPING); // This needs to be per-SwapType
|
sendBroadcast(SwapService.EXTRA_STOPPING); // This needs to be per-SwapType
|
||||||
Utils.debugLog(TAG, "Sending message to swap webserver to stop it.");
|
|
||||||
LocalHTTPDManager.stop(context);
|
LocalHTTPDManager.stop(context);
|
||||||
|
BonjourManager.stop(context);
|
||||||
// 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);
|
setConnected(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import cc.mvdan.accesspoint.WifiApControl;
|
|||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
|
import org.fdroid.fdroid.localrepo.BonjourManager;
|
||||||
import org.fdroid.fdroid.localrepo.SwapService;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
import org.fdroid.fdroid.localrepo.SwapView;
|
import org.fdroid.fdroid.localrepo.SwapView;
|
||||||
import org.fdroid.fdroid.localrepo.peers.Peer;
|
import org.fdroid.fdroid.localrepo.peers.Peer;
|
||||||
@ -318,15 +319,15 @@ public class StartSwapView extends SwapView {
|
|||||||
viewWifiNetwork = (TextView) findViewById(R.id.wifi_network);
|
viewWifiNetwork = (TextView) findViewById(R.id.wifi_network);
|
||||||
|
|
||||||
wifiSwitch = (SwitchCompat) findViewById(R.id.switch_wifi);
|
wifiSwitch = (SwitchCompat) findViewById(R.id.switch_wifi);
|
||||||
wifiSwitch.setOnCheckedChangeListener(onWifiSwitchToggled);
|
wifiSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||||
setWifiSwitchState(getActivity().getSwapService().isBonjourDiscoverable(), true);
|
@Override
|
||||||
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
|
BonjourManager.setVisible(getContext(), isChecked);
|
||||||
|
SwapService.putWifiVisibleUserPreference(isChecked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
textWifiVisible = findViewById(R.id.wifi_visible);
|
textWifiVisible = findViewById(R.id.wifi_visible);
|
||||||
if (getActivity().getSwapService().isBonjourDiscoverable()) {
|
|
||||||
textWifiVisible.setText(R.string.swap_visible_wifi);
|
|
||||||
} else {
|
|
||||||
textWifiVisible.setText(R.string.swap_not_visible_wifi);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that this is only listening for the WifiSwap, whereas we start both the WifiSwap
|
// Note that this is only listening for the WifiSwap, whereas we start both the WifiSwap
|
||||||
// and the Bonjour service at the same time. Technically swap will work fine without
|
// and the Bonjour service at the same time. Technically swap will work fine without
|
||||||
|
@ -816,8 +816,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
} else {
|
} else {
|
||||||
String bluetooth = service.getBluetoothSwap().isConnected() ? "Y" : " N";
|
String bluetooth = service.getBluetoothSwap().isConnected() ? "Y" : " N";
|
||||||
String wifi = service.getWifiSwap().isConnected() ? "Y" : " N";
|
String wifi = service.getWifiSwap().isConnected() ? "Y" : " N";
|
||||||
String mdns = service.getWifiSwap().getBonjour().isConnected() ? "Y" : " N";
|
|
||||||
message += "Swap { BT: " + bluetooth + ", WiFi: " + wifi + ", mDNS: " + mdns + "}, ";
|
|
||||||
|
|
||||||
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
bluetooth = "N/A";
|
bluetooth = "N/A";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user